From b6c4590531d6776a083c15e25e1412b26c9c5b95 Mon Sep 17 00:00:00 2001 From: psej Date: Mon, 27 Nov 2023 15:46:10 +0100 Subject: [PATCH 001/133] implemented ComplexDouble, added LibMatrixFourier --- .../org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java | 2 ++ .../apache/sysds/runtime/matrix/data/sketch/ComplexDouble.java | 2 ++ 2 files changed, 4 insertions(+) create mode 100644 src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java create mode 100644 src/main/java/org/apache/sysds/runtime/matrix/data/sketch/ComplexDouble.java diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java new file mode 100644 index 00000000000..04d44d063c7 --- /dev/null +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java @@ -0,0 +1,2 @@ +package org.apache.sysds.runtime.matrix.data;public class LibMatrixFourier { +} diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/sketch/ComplexDouble.java b/src/main/java/org/apache/sysds/runtime/matrix/data/sketch/ComplexDouble.java new file mode 100644 index 00000000000..3d7616ef6f5 --- /dev/null +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/sketch/ComplexDouble.java @@ -0,0 +1,2 @@ +package org.apache.sysds.runtime.matrix.data.sketch;public class ComplexDouble { +} From 189208c04f5449136efc203f1138cda7b293dae7 Mon Sep 17 00:00:00 2001 From: psej Date: Mon, 27 Nov 2023 18:09:34 +0100 Subject: [PATCH 002/133] added pow for ComplexDouble, updated fft --- .../runtime/matrix/data/LibMatrixFourier.java | 88 ++++++++++++++++++- .../matrix/data/sketch/ComplexDouble.java | 39 +++++++- 2 files changed, 125 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java index 04d44d063c7..512b4dbd911 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java @@ -1,2 +1,88 @@ -package org.apache.sysds.runtime.matrix.data;public class LibMatrixFourier { +package org.apache.sysds.runtime.matrix.data; + +import org.apache.sysds.runtime.matrix.data.sketch.ComplexDouble; + +public class LibMatrixFourier { + + /** + * Function to perform Fast Fourier Transformation on a given array. + * + * @param in array of ComplexDoubles + * @return array of ComplexDoubles + */ + public ComplexDouble[] fft(ComplexDouble[] in){ + + in = fillToPowerOfTwo(in); + int n = in.length; + if(n == 1){ + return in; + } + + double alpha = 2*Math.PI/n; + ComplexDouble omega = new ComplexDouble(Math.cos(alpha), Math.sin(alpha)); + + ComplexDouble[] even = new ComplexDouble[n/2]; + ComplexDouble[] odd = new ComplexDouble[n/2]; + for(int i=0; i < n/2; i++){ + even[i] = in[i*2]; + odd[i] = in[i*2+1]; + } + ComplexDouble[] resEven = fft(even); + ComplexDouble[] resOdd = fft(odd); + + ComplexDouble[] res = new ComplexDouble[n]; + for(int j=0; j < n/2; j++){ + res[j] = resEven[j].add(omega.pow(j).mul(resOdd[j])); + res[j+n/2] = resEven[j].sub(omega.pow(j).mul(resOdd[j])); + } + return res; + } + + /** + * Function to perform Fast Fourier Transformation on a given array. + * + * @param in array of doubles + * @return array of ComplexDoubles + */ + public ComplexDouble[] fft(double[] in){ + ComplexDouble[] complex = new ComplexDouble[in.length]; + for(int i=0; i Date: Mon, 27 Nov 2023 19:12:50 +0100 Subject: [PATCH 003/133] added complexDouble functions, simple Test --- .../data/{sketch => }/ComplexDouble.java | 17 +++++++++- .../runtime/matrix/data/LibMatrixFourier.java | 16 ++++------ .../test/component/matrix/FourierTest.java | 32 +++++++++++++++++++ 3 files changed, 55 insertions(+), 10 deletions(-) rename src/main/java/org/apache/sysds/runtime/matrix/data/{sketch => }/ComplexDouble.java (70%) create mode 100644 src/test/java/org/apache/sysds/test/component/matrix/FourierTest.java diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/sketch/ComplexDouble.java b/src/main/java/org/apache/sysds/runtime/matrix/data/ComplexDouble.java similarity index 70% rename from src/main/java/org/apache/sysds/runtime/matrix/data/sketch/ComplexDouble.java rename to src/main/java/org/apache/sysds/runtime/matrix/data/ComplexDouble.java index 4b7cc340503..1ffa4a10454 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/sketch/ComplexDouble.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/ComplexDouble.java @@ -1,4 +1,4 @@ -package org.apache.sysds.runtime.matrix.data.sketch; +package org.apache.sysds.runtime.matrix.data; public class ComplexDouble { double re; @@ -36,4 +36,19 @@ public ComplexDouble pow(int n){ return new ComplexDouble(Math.pow(dist,n) * Math.cos(n*angle),Math.pow(dist,n) * Math.sin(n*angle)); } + @Override + public String toString() { + return this.re + " + " + this.im + "i"; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ComplexDouble that = (ComplexDouble) o; + + double epsilon = 0.000001d; + return Math.abs(this.re - that.re) < epsilon && Math.abs(this.im - that.im) < epsilon; + } + } diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java index 512b4dbd911..c1138a30f25 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java @@ -1,7 +1,5 @@ package org.apache.sysds.runtime.matrix.data; -import org.apache.sysds.runtime.matrix.data.sketch.ComplexDouble; - public class LibMatrixFourier { /** @@ -10,7 +8,7 @@ public class LibMatrixFourier { * @param in array of ComplexDoubles * @return array of ComplexDoubles */ - public ComplexDouble[] fft(ComplexDouble[] in){ + public static ComplexDouble[] fft(ComplexDouble[] in){ in = fillToPowerOfTwo(in); int n = in.length; @@ -18,8 +16,8 @@ public ComplexDouble[] fft(ComplexDouble[] in){ return in; } - double alpha = 2*Math.PI/n; - ComplexDouble omega = new ComplexDouble(Math.cos(alpha), Math.sin(alpha)); + double angle = 2*Math.PI/n; + ComplexDouble omega = new ComplexDouble(Math.cos(angle), Math.sin(angle)); ComplexDouble[] even = new ComplexDouble[n/2]; ComplexDouble[] odd = new ComplexDouble[n/2]; @@ -44,7 +42,7 @@ public ComplexDouble[] fft(ComplexDouble[] in){ * @param in array of doubles * @return array of ComplexDoubles */ - public ComplexDouble[] fft(double[] in){ + public static ComplexDouble[] fft(double[] in){ ComplexDouble[] complex = new ComplexDouble[in.length]; for(int i=0; i Date: Mon, 27 Nov 2023 19:57:39 +0100 Subject: [PATCH 004/133] TODO: length of non power of two, added test --- .../runtime/matrix/data/LibMatrixFourier.java | 6 +++++- .../test/component/matrix/FourierTest.java | 19 +++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java index c1138a30f25..fe7fe43c91c 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java @@ -4,13 +4,16 @@ public class LibMatrixFourier { /** * Function to perform Fast Fourier Transformation on a given array. + * Its length has to be a power of two. * * @param in array of ComplexDoubles * @return array of ComplexDoubles */ public static ComplexDouble[] fft(ComplexDouble[] in){ - in = fillToPowerOfTwo(in); + // TODO: how to invert fillToPowerOfTwo after calculation + // in = fillToPowerOfTwo(in); + int n = in.length; if(n == 1){ return in; @@ -38,6 +41,7 @@ public static ComplexDouble[] fft(ComplexDouble[] in){ /** * Function to perform Fast Fourier Transformation on a given array. + * Its length has to be a power of two. * * @param in array of doubles * @return array of ComplexDoubles diff --git a/src/test/java/org/apache/sysds/test/component/matrix/FourierTest.java b/src/test/java/org/apache/sysds/test/component/matrix/FourierTest.java index 148d8caaca6..50633ad2acd 100644 --- a/src/test/java/org/apache/sysds/test/component/matrix/FourierTest.java +++ b/src/test/java/org/apache/sysds/test/component/matrix/FourierTest.java @@ -29,4 +29,23 @@ public void simpleTest() { assertTrue(Arrays.equals(expected, res)); } + @Test + public void notPowerOfTwoTest() { + double[] in = {1, 2, 3, 4, 5}; + + // see https://de.mathworks.com/help/matlab/ref/ifft.html + ComplexDouble[] expected = new ComplexDouble[5]; + expected[0] = new ComplexDouble(15,0); + expected[1] = new ComplexDouble(-2.5000,3.4410); + expected[2] = new ComplexDouble(-2.5000,0.8123); + expected[3] = new ComplexDouble(-2.5000, - 0.8123); + expected[4] = new ComplexDouble(-2.5000, - 3.4410); + + ComplexDouble[] res = fft(in); + for(ComplexDouble elem : res){ + System.out.println(elem); + } + assertTrue(Arrays.equals(expected, res)); + } + } From d185a105d4580fb74e22e23192024bf0f5a082a6 Mon Sep 17 00:00:00 2001 From: psej Date: Mon, 11 Dec 2023 21:57:48 +0100 Subject: [PATCH 005/133] implemented fft2d + simple test --- .../runtime/matrix/data/LibMatrixFourier.java | 65 +++++++++++++++++++ .../test/component/matrix/FourierTest.java | 25 ++++++- 2 files changed, 88 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java index fe7fe43c91c..a3bc0571b55 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java @@ -87,4 +87,69 @@ private static int nextPowerOfTwo(int n){ return res; } + /** + * Function to perform Fast Fourier Transformation in a 2-dimensional array. + * Both dimensions have to be a power of two. + * First fft is applied to each row, then fft is applied to each column of the previous result. + * + * @param in 2-dimensional array of ComplexDoubles + * @return 2-dimensional array of ComplexDoubles + */ + public static ComplexDouble[][] fft2d(ComplexDouble[][] in) { + + // TODO: Is it sufficient to use only real parts of the result for further computations? + int rows = in.length; + int cols = in[0].length; + + ComplexDouble[][] out = new ComplexDouble[rows][cols]; + + for(int i = 0; i < rows; i++){ + // use fft on row + ComplexDouble[] resRow = fft(in[i]); + // only use real part of result for further computations + for(int j = 0; j < cols; j++){ + out[i][j] = resRow[j]; + //out[i][j] = new ComplexDouble(resRow[j].re, 0); + } + } + + for(int j = 0; j < cols; j++){ + // get col as array + ComplexDouble[] inCol = new ComplexDouble[rows]; + for(int i = 0; i < rows; i++){ + inCol[i] = out[i][j]; + } + // use fft on col + ComplexDouble[] resCol = fft(inCol); + for (int i = 0; i < rows; i++) { + out[i][j] = resCol[i]; + //out[i][j] = new ComplexDouble(resCol[i].re, 0); + } + } + + return out; + } + + /** + * Function to perform Fast Fourier Transformation in a 2-dimensional array. + * Both dimensions have to be a power of two. + * First fft is applied to each row, then fft is applied to each column of the previous result. + * + * @param in 2-dimensional array of doubles + * @return 2-dimensional array of ComplexDoubles + */ + public static ComplexDouble[][] fft2d(double[][] in){ + int rows = in.length; + int cols = in[0].length; + + ComplexDouble[][] complex = new ComplexDouble[rows][cols]; + for(int i=0; i Date: Mon, 11 Dec 2023 22:19:15 +0100 Subject: [PATCH 006/133] second test fft2d --- .../runtime/matrix/data/LibMatrixFourier.java | 9 +------ .../test/component/matrix/FourierTest.java | 24 +++++++++++++++++++ 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java index a3bc0571b55..30adbb3dbc5 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java @@ -97,7 +97,6 @@ private static int nextPowerOfTwo(int n){ */ public static ComplexDouble[][] fft2d(ComplexDouble[][] in) { - // TODO: Is it sufficient to use only real parts of the result for further computations? int rows = in.length; int cols = in[0].length; @@ -105,12 +104,7 @@ public static ComplexDouble[][] fft2d(ComplexDouble[][] in) { for(int i = 0; i < rows; i++){ // use fft on row - ComplexDouble[] resRow = fft(in[i]); - // only use real part of result for further computations - for(int j = 0; j < cols; j++){ - out[i][j] = resRow[j]; - //out[i][j] = new ComplexDouble(resRow[j].re, 0); - } + out[i] = fft(in[i]); } for(int j = 0; j < cols; j++){ @@ -123,7 +117,6 @@ public static ComplexDouble[][] fft2d(ComplexDouble[][] in) { ComplexDouble[] resCol = fft(inCol); for (int i = 0; i < rows; i++) { out[i][j] = resCol[i]; - //out[i][j] = new ComplexDouble(resCol[i].re, 0); } } diff --git a/src/test/java/org/apache/sysds/test/component/matrix/FourierTest.java b/src/test/java/org/apache/sysds/test/component/matrix/FourierTest.java index f38ed186c4d..7b10df0c2ab 100644 --- a/src/test/java/org/apache/sysds/test/component/matrix/FourierTest.java +++ b/src/test/java/org/apache/sysds/test/component/matrix/FourierTest.java @@ -52,6 +52,7 @@ public void notPowerOfTwoTest() { @Test public void simple2dTest() { + // tested with matlab double[][] in = {{0, 18}, {-15, 3}}; ComplexDouble[][] expected = new ComplexDouble[2][2]; expected[0][0] = new ComplexDouble(6, 0); @@ -69,4 +70,27 @@ public void simple2dTest() { assertArrayEquals(expected, res); } + @Test + public void simple2dTest2() { + double[][] in = {{0, 18, -15, 3}, {0, 18, -15, 3}}; + ComplexDouble[][] expected = new ComplexDouble[2][4]; + expected[0][0] = new ComplexDouble(12, 0); + expected[0][1] = new ComplexDouble(30, -30); + expected[0][2] = new ComplexDouble(-72, 0); + expected[0][3] = new ComplexDouble(30, 30); + expected[1][0] = new ComplexDouble(0, 0); + expected[1][1] = new ComplexDouble(0, 0); + expected[1][2] = new ComplexDouble(0, 0); + expected[1][3] = new ComplexDouble(0, 0); + + ComplexDouble[][] res = fft2d(in); + for(ComplexDouble[] row : res){ + for(ComplexDouble elem : row){ + System.out.println(elem); + } + } + + assertArrayEquals(expected, res); + } + } From 51c95a49c7d7cd9c05d71fe1fb3b829ad2337f9e Mon Sep 17 00:00:00 2001 From: Frederic Zoepffel Date: Sun, 17 Dec 2023 23:14:46 +0100 Subject: [PATCH 007/133] added numpy function to create fft output to feed in java test. added more methods to complexDouble and added ifft --- pom.xml | 6 + .../runtime/matrix/data/ComplexDouble.java | 41 +++++-- .../runtime/matrix/data/LibMatrixFourier.java | 69 +++++++++++- .../test/component/matrix/FourierTest.java | 106 +++++++++++++++++- .../test/component/matrix/FourierTestData.py | 35 ++++++ 5 files changed, 246 insertions(+), 11 deletions(-) create mode 100644 src/test/java/org/apache/sysds/test/component/matrix/FourierTestData.py diff --git a/pom.xml b/pom.xml index b97cfb30ada..4cacb56fb37 100644 --- a/pom.xml +++ b/pom.xml @@ -875,6 +875,12 @@ + + org.json + json + 20210307 + + org.jcuda jcuda diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/ComplexDouble.java b/src/main/java/org/apache/sysds/runtime/matrix/data/ComplexDouble.java index 1ffa4a10454..7fd09d3f942 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/ComplexDouble.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/ComplexDouble.java @@ -1,8 +1,8 @@ package org.apache.sysds.runtime.matrix.data; public class ComplexDouble { - double re; - double im; + public double re; + public double im; public ComplexDouble(double re, double im){ this.re = re; @@ -28,14 +28,41 @@ public ComplexDouble mul(ComplexDouble other){ * @return the n-th power of the complex double */ public ComplexDouble pow(int n){ - // compute polar form - double dist = Math.sqrt(Math.pow(this.re,2) + Math.pow(this.im,2)); - double angle = Math.acos(this.re/dist); + double dist = Math.sqrt(this.re * this.re + this.im * this.im); + double angle = Math.atan2(this.im, this.re); + + return new ComplexDouble(Math.pow(dist, n) * Math.cos(n * angle), + Math.pow(dist, n) * Math.sin(n * angle)); + } + + // Division + public ComplexDouble div(ComplexDouble other) { + double denominator = other.re * other.re + other.im * other.im; + return new ComplexDouble((this.re * other.re + this.im * other.im) / denominator, + (this.im * other.re - this.re * other.im) / denominator); + } + + // Conjugate + public ComplexDouble conjugate() { + return new ComplexDouble(this.re, -this.im); + } - // de moivre’s theorem - return new ComplexDouble(Math.pow(dist,n) * Math.cos(n*angle),Math.pow(dist,n) * Math.sin(n*angle)); + // Absolute Value + public double abs() { + return Math.sqrt(this.re * this.re + this.im * this.im); } + // Argument (Phase) + public double arg() { + return Math.atan2(this.im, this.re); + } + + // Polar Form Conversion + public static ComplexDouble fromPolar(double magnitude, double angle) { + return new ComplexDouble(magnitude * Math.cos(angle), magnitude * Math.sin(angle)); + } + + @Override public String toString() { return this.re + " + " + this.im + "i"; diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java index 30adbb3dbc5..7f4480d97fd 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java @@ -36,7 +36,7 @@ public static ComplexDouble[] fft(ComplexDouble[] in){ res[j] = resEven[j].add(omega.pow(j).mul(resOdd[j])); res[j+n/2] = resEven[j].sub(omega.pow(j).mul(resOdd[j])); } - return res; + return res.conjugate(); } /** @@ -54,6 +54,38 @@ public static ComplexDouble[] fft(double[] in){ return fft(complex); } + + /** + * Function to perform Inverse Fast Fourier Transformation on a given array. + * Its length has to be a power of two. + * + * @param in array of ComplexDoubles + * @return array of ComplexDoubles + */ + public static ComplexDouble[] ifft(ComplexDouble[] in) { + int n = in.length; + + // Take conjugate + for (int i = 0; i < n; i++) { + in[i] = in[i].conjugate(); + } + + // Compute forward FFT + ComplexDouble[] out = fft(in); + + // Take conjugate again + for (int i = 0; i < n; i++) { + out[i] = out[i].conjugate(); + } + + // Scale by n + for (int i = 0; i < n; i++) { + out[i] = new ComplexDouble(out[i].re / n, out[i].im / n); + } + + return out; + } + /** * Function to fill a given array of ComplexDoubles with 0-ComplexDoubles, so that the length is a power of two. * Needed for FastFourierTransformation @@ -144,5 +176,40 @@ public static ComplexDouble[][] fft2d(double[][] in){ return fft2d(complex); } + /** + * Function to perform Inverse Fast Fourier Transformation in a 2-dimensional array. + * Both dimensions have to be a power of two. + * First ifft is applied to each row, then ifft is applied to each column of the previous result. + * + * @param in 2-dimensional array of ComplexDoubles + * @return 2-dimensional array of ComplexDoubles + */ + public static ComplexDouble[][] ifft2d(ComplexDouble[][] in) { + int rows = in.length; + int cols = in[0].length; + + ComplexDouble[][] out = new ComplexDouble[rows][cols]; + + // Apply IFFT to each row + for (int i = 0; i < rows; i++) { + out[i] = ifft(in[i]); + } + + // Apply IFFT to each column + for (int j = 0; j < cols; j++) { + ComplexDouble[] col = new ComplexDouble[rows]; + for (int i = 0; i < rows; i++) { + col[i] = out[i][j]; + } + + ComplexDouble[] resCol = ifft(col); + for (int i = 0; i < rows; i++) { + out[i][j] = resCol[i]; + } + } + + return out; + } + } diff --git a/src/test/java/org/apache/sysds/test/component/matrix/FourierTest.java b/src/test/java/org/apache/sysds/test/component/matrix/FourierTest.java index 7b10df0c2ab..32c2425521d 100644 --- a/src/test/java/org/apache/sysds/test/component/matrix/FourierTest.java +++ b/src/test/java/org/apache/sysds/test/component/matrix/FourierTest.java @@ -1,19 +1,76 @@ package org.apache.sysds.test.component.matrix; import org.apache.sysds.runtime.matrix.data.ComplexDouble; -import org.apache.sysds.runtime.matrix.data.LibMatrixFourier; import static org.apache.sysds.runtime.matrix.data.LibMatrixFourier.fft; import static org.apache.sysds.runtime.matrix.data.LibMatrixFourier.fft2d; +import static org.apache.sysds.runtime.matrix.data.LibMatrixFourier.ifft; +import static org.apache.sysds.runtime.matrix.data.LibMatrixFourier.ifft2d; import org.junit.Test; -import java.util.Arrays; - import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertTrue; +import java.io.BufferedReader; +import java.io.FileReader; +import java.io.IOException; + public class FourierTest { + @Test + public void testFftWithNumpyData() throws IOException { + String filename = "fft_data.csv"; // path to your CSV file + BufferedReader reader = new BufferedReader(new FileReader(filename)); + String line; + int lineNumber = 0; + + while ((line = reader.readLine()) != null) { + lineNumber++; + if (lineNumber % 1000 == 0) { // Print progress every 1000 lines + System.out.println("Processing line: " + lineNumber); + } + + String[] values = line.split(","); + int n = values.length / 3; + double[] input = new double[n]; + ComplexDouble[] expected = new ComplexDouble[n]; + ComplexDouble[] actual; + + for (int i = 0; i < n; i++) { + input[i] = Double.parseDouble(values[i]); + double real = Double.parseDouble(values[n + i]); + double imag = Double.parseDouble(values[n * 2 + i]); + expected[i] = new ComplexDouble(real, imag); + } + + actual = fft(input); + + for (int i = 0; i < n; i++) { + assertComplexEquals("Mismatch at index " + i + " in line " + lineNumber, expected[i], actual[i]); + } + } + + reader.close(); + System.out.println("Finished processing " + lineNumber + " lines."); + } + + private void assertComplexEquals(String message, ComplexDouble expected, ComplexDouble actual) { + final double EPSILON = 0.000001; + boolean realMatch = Math.abs(Math.abs(expected.re) - Math.abs(actual.re)) < EPSILON; + boolean imagMatch = Math.abs(Math.abs(expected.im) - Math.abs(actual.im)) < EPSILON; + + if (realMatch && imagMatch) { + if (Math.signum(expected.re) != Math.signum(actual.re)) { + System.out.println(message + " - Real part is of opposite sign but otherwise correct"); + } + if (Math.signum(expected.im) != Math.signum(actual.im)) { + System.out.println(message + " - Imaginary part is of opposite sign but otherwise correct"); + } + } else { + assertTrue(message + " - Incorrect values", false); + } + } + @Test public void simpleTest() { double[] in = {0, 18, -15, 3}; @@ -93,4 +150,47 @@ public void simple2dTest2() { assertArrayEquals(expected, res); } + @Test + public void testSimpleIfft1d() { + double[] original = {1, -2, 3, -4}; + ComplexDouble[] complexInput = new ComplexDouble[original.length]; + for (int i = 0; i < original.length; i++) { + complexInput[i] = new ComplexDouble(original[i], 0); + } + + ComplexDouble[] fftResult = fft(complexInput); + ComplexDouble[] ifftResult = ifft(fftResult); + + for (int i = 0; i < original.length; i++) { + assertEquals("Mismatch at index " + i, original[i], ifftResult[i].re, 0.000001); + assertEquals("Non-zero imaginary part at index " + i, 0, ifftResult[i].im, 0.000001); + } + } + + @Test + public void testSimpleIfft2d() { + double[][] original = {{1, -2}, {3, -4}}; + ComplexDouble[][] complexInput = new ComplexDouble[original.length][original[0].length]; + for (int i = 0; i < original.length; i++) { + for (int j = 0; j < original[0].length; j++) { + complexInput[i][j] = new ComplexDouble(original[i][j], 0); + } + } + + ComplexDouble[][] fftResult = fft2d(complexInput); + ComplexDouble[][] ifftResult = ifft2d(fftResult); + + for (int i = 0; i < original.length; i++) { + for (int j = 0; j < original[0].length; j++) { + assertEquals("Mismatch at [" + i + "][" + j + "]", original[i][j], ifftResult[i][j].re, 0.000001); + assertEquals("Non-zero imaginary part at [" + i + "][" + j + "]", 0, ifftResult[i][j].im, 0.000001); + } + } + } + + // Helper method for asserting equality with a tolerance + private static void assertEquals(String message, double expected, double actual, double tolerance) { + assertTrue(message + " - Expected: " + expected + ", Actual: " + actual, Math.abs(expected - actual) <= tolerance); + } + } diff --git a/src/test/java/org/apache/sysds/test/component/matrix/FourierTestData.py b/src/test/java/org/apache/sysds/test/component/matrix/FourierTestData.py new file mode 100644 index 00000000000..bd7a1bef5e0 --- /dev/null +++ b/src/test/java/org/apache/sysds/test/component/matrix/FourierTestData.py @@ -0,0 +1,35 @@ +import numpy as np +import csv + +def generate_inputs(num_inputs, max_power): + for _ in range(num_inputs): + power = np.random.randint(1, max_power+1) + length = 2 ** power + yield np.random.rand(length) # generate array of random floats + +def compute_fft(inputs): + for input_array in inputs: + yield np.fft.fft(input_array) + +def save_to_file(inputs, outputs, filename, mode='a'): + with open(filename, mode, newline='') as file: + writer = csv.writer(file) + for input_array, output_array in zip(inputs, outputs): + flattened_data = np.concatenate((input_array, output_array.real, output_array.imag)) + writer.writerow(flattened_data) + +# Parameters +num_inputs = 100000 +batch_size = 10000 +max_power = 10 # example max power of 2 for input length +filename = "fft_data.csv" + +# Process and save in batches +for i in range(0, num_inputs, batch_size): + current_batch = min(batch_size, num_inputs - i) + inputs = list(generate_inputs(current_batch, max_power)) + outputs = list(compute_fft(inputs)) + save_to_file(inputs, outputs, filename, mode='a' if i > 0 else 'w') + print(f"Batch {i//batch_size + 1} out of {num_inputs/batch_size} saved to {filename}") + +print("All data processed and saved.") \ No newline at end of file From e46c73b0b41ce8c9c877913068dd7fa2b8d3648b Mon Sep 17 00:00:00 2001 From: Frederic Zoepffel Date: Mon, 18 Dec 2023 00:18:56 +0100 Subject: [PATCH 008/133] added execution time checker --- .../runtime/matrix/data/LibMatrixFourier.java | 3 +- .../test/component/matrix/FourierTest.java | 43 ++++++++++++++++++- .../test/component/matrix/FourierTestData.py | 19 +++++++- 3 files changed, 61 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java index 7f4480d97fd..ae8a3ea6cde 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java @@ -36,7 +36,8 @@ public static ComplexDouble[] fft(ComplexDouble[] in){ res[j] = resEven[j].add(omega.pow(j).mul(resOdd[j])); res[j+n/2] = resEven[j].sub(omega.pow(j).mul(resOdd[j])); } - return res.conjugate(); + + return res; } /** diff --git a/src/test/java/org/apache/sysds/test/component/matrix/FourierTest.java b/src/test/java/org/apache/sysds/test/component/matrix/FourierTest.java index 32c2425521d..529e27c5eb0 100644 --- a/src/test/java/org/apache/sysds/test/component/matrix/FourierTest.java +++ b/src/test/java/org/apache/sysds/test/component/matrix/FourierTest.java @@ -16,7 +16,7 @@ import java.io.IOException; public class FourierTest { - + @Test public void testFftWithNumpyData() throws IOException { String filename = "fft_data.csv"; // path to your CSV file @@ -54,6 +54,47 @@ public void testFftWithNumpyData() throws IOException { System.out.println("Finished processing " + lineNumber + " lines."); } + + @Test + public void testFftExecutionTime() throws IOException { + String filename = "fft_data.csv"; // path to your CSV file + BufferedReader reader = new BufferedReader(new FileReader(filename)); + String line; + int lineNumber = 0; + long totalTime = 0; // Total time for all FFT computations + int numCalculations = 0; // Number of FFT computations + + while ((line = reader.readLine()) != null) { + lineNumber++; + String[] values = line.split(","); + int n = values.length / 3; + double[] input = new double[n]; + ComplexDouble[] expected = new ComplexDouble[n]; + + for (int i = 0; i < n; i++) { + input[i] = Double.parseDouble(values[i]); + double real = Double.parseDouble(values[n + i]); + double imag = Double.parseDouble(values[n * 2 + i]); + expected[i] = new ComplexDouble(real, imag); + } + + long startTime = System.nanoTime(); + fft(input); + long endTime = System.nanoTime(); + totalTime += (endTime - startTime); + numCalculations++; + + + if (numCalculations % 1000 == 0) { + double averageTime = (totalTime / 1e6) / numCalculations; // Average time in milliseconds + System.out.println("\n\n\n\n\n\n\n\nAverage execution time after " + numCalculations + " calculations: " + averageTime + " ms \n\n\n\n\n\n\n\n"); + } + } + + reader.close(); + System.out.println("Finished processing " + lineNumber + " lines."); + } + private void assertComplexEquals(String message, ComplexDouble expected, ComplexDouble actual) { final double EPSILON = 0.000001; boolean realMatch = Math.abs(Math.abs(expected.re) - Math.abs(actual.re)) < EPSILON; diff --git a/src/test/java/org/apache/sysds/test/component/matrix/FourierTestData.py b/src/test/java/org/apache/sysds/test/component/matrix/FourierTestData.py index bd7a1bef5e0..289995f3561 100644 --- a/src/test/java/org/apache/sysds/test/component/matrix/FourierTestData.py +++ b/src/test/java/org/apache/sysds/test/component/matrix/FourierTestData.py @@ -1,5 +1,6 @@ import numpy as np import csv +import time def generate_inputs(num_inputs, max_power): for _ in range(num_inputs): @@ -8,8 +9,22 @@ def generate_inputs(num_inputs, max_power): yield np.random.rand(length) # generate array of random floats def compute_fft(inputs): + total_time = 0 + num_calculations = 0 + for input_array in inputs: - yield np.fft.fft(input_array) + start_time = time.time() + result = np.fft.fft(input_array) + end_time = time.time() + + total_time += end_time - start_time + num_calculations += 1 + + if num_calculations % 1000 == 0: + average_time = total_time / num_calculations + print(f"Average execution time after {num_calculations} calculations: {average_time:.6f} seconds") + + yield result def save_to_file(inputs, outputs, filename, mode='a'): with open(filename, mode, newline='') as file: @@ -30,6 +45,6 @@ def save_to_file(inputs, outputs, filename, mode='a'): inputs = list(generate_inputs(current_batch, max_power)) outputs = list(compute_fft(inputs)) save_to_file(inputs, outputs, filename, mode='a' if i > 0 else 'w') - print(f"Batch {i//batch_size + 1} out of {num_inputs/batch_size} saved to {filename}") + print(f"Batch {i//batch_size + 1} out of {num_inputs//batch_size} saved to {filename}") print("All data processed and saved.") \ No newline at end of file From f61bcd4d0461a46028af5b876955588014850b27 Mon Sep 17 00:00:00 2001 From: psej Date: Mon, 18 Dec 2023 01:51:35 +0100 Subject: [PATCH 009/133] fixed fft + fft2d --- .../runtime/matrix/data/LibMatrixFourier.java | 9 ++++--- .../test/component/matrix/FourierTest.java | 26 +++++++++++++++++-- 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java index 30adbb3dbc5..89ec176668d 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java @@ -19,8 +19,7 @@ public static ComplexDouble[] fft(ComplexDouble[] in){ return in; } - double angle = 2*Math.PI/n; - ComplexDouble omega = new ComplexDouble(Math.cos(angle), Math.sin(angle)); + double angle = -2*Math.PI/n; ComplexDouble[] even = new ComplexDouble[n/2]; ComplexDouble[] odd = new ComplexDouble[n/2]; @@ -33,8 +32,10 @@ public static ComplexDouble[] fft(ComplexDouble[] in){ ComplexDouble[] res = new ComplexDouble[n]; for(int j=0; j < n/2; j++){ - res[j] = resEven[j].add(omega.pow(j).mul(resOdd[j])); - res[j+n/2] = resEven[j].sub(omega.pow(j).mul(resOdd[j])); + ComplexDouble omegaPow = new ComplexDouble(Math.cos(j*angle), Math.sin(j*angle)); + + res[j] = resEven[j].add(omegaPow.mul(resOdd[j])); + res[j+n/2] = resEven[j].sub(omegaPow.mul(resOdd[j])); } return res; } diff --git a/src/test/java/org/apache/sysds/test/component/matrix/FourierTest.java b/src/test/java/org/apache/sysds/test/component/matrix/FourierTest.java index 7b10df0c2ab..0995c69de4a 100644 --- a/src/test/java/org/apache/sysds/test/component/matrix/FourierTest.java +++ b/src/test/java/org/apache/sysds/test/component/matrix/FourierTest.java @@ -16,12 +16,13 @@ public class FourierTest { @Test public void simpleTest() { + // tested with numpy double[] in = {0, 18, -15, 3}; ComplexDouble[] expected = new ComplexDouble[4]; expected[0] = new ComplexDouble(6, 0); - expected[1] = new ComplexDouble(15, 15); + expected[1] = new ComplexDouble(15, -15); expected[2] = new ComplexDouble(-36, 0); - expected[3] = new ComplexDouble(15, -15); + expected[3] = new ComplexDouble(15, 15); ComplexDouble[] res = fft(in); for(ComplexDouble elem : res){ @@ -93,4 +94,25 @@ public void simple2dTest2() { assertArrayEquals(expected, res); } + @Test + public void simple2dTest2SecondPart() { + // ComplexDouble(15, -15) is the second (expected[1]) entry of simpleTest + // this tests the col computation in fft2d for simple2dTest2 + + ComplexDouble[] in = new ComplexDouble[2]; + in[0] = new ComplexDouble(15, -15); + in[1] = new ComplexDouble(15, -15); + + ComplexDouble[] expected = new ComplexDouble[2]; + expected[0] = new ComplexDouble(30, -30); + expected[1] = new ComplexDouble(0, 0); + + ComplexDouble[] res = fft(in); + for(ComplexDouble elem : res){ + System.out.println(elem); + } + + assertArrayEquals(expected, res); + } + } From 8d959fc0502d52a07c06cb614e3dd2014acbcdc4 Mon Sep 17 00:00:00 2001 From: Frederic Zoepffel Date: Mon, 18 Dec 2023 13:10:55 +0100 Subject: [PATCH 010/133] added ifft and tests --- .../runtime/matrix/data/LibMatrixFourier.java | 47 ++--- .../test/component/matrix/FourierTest.java | 173 +++++++++++++++++- .../test/component/matrix/FourierTestData.py | 63 ++++++- 3 files changed, 252 insertions(+), 31 deletions(-) diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java index c70fefdc552..4162c26ca57 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java @@ -57,35 +57,38 @@ public static ComplexDouble[] fft(double[] in){ } - /** - * Function to perform Inverse Fast Fourier Transformation on a given array. - * Its length has to be a power of two. - * - * @param in array of ComplexDoubles - * @return array of ComplexDoubles - */ public static ComplexDouble[] ifft(ComplexDouble[] in) { int n = in.length; - - // Take conjugate + + // Conjugate the input array + ComplexDouble[] conjugatedInput = new ComplexDouble[n]; for (int i = 0; i < n; i++) { - in[i] = in[i].conjugate(); + conjugatedInput[i] = in[i].conjugate(); } - - // Compute forward FFT - ComplexDouble[] out = fft(in); - - // Take conjugate again + + // Apply FFT to conjugated input + ComplexDouble[] fftResult = fft(conjugatedInput); + + // Conjugate the result of FFT and scale by n + ComplexDouble[] ifftResult = new ComplexDouble[n]; for (int i = 0; i < n; i++) { - out[i] = out[i].conjugate(); + ifftResult[i] = new ComplexDouble(fftResult[i].re / n, -fftResult[i].im / n); } - - // Scale by n - for (int i = 0; i < n; i++) { - out[i] = new ComplexDouble(out[i].re / n, out[i].im / n); + + return ifftResult; + } + + /** + * IFFT for real-valued input. + * @param in array of doubles + * @return array of ComplexDoubles representing the IFFT + */ + public static ComplexDouble[] ifft(double[] in) { + ComplexDouble[] complexIn = new ComplexDouble[in.length]; + for (int i = 0; i < in.length; i++) { + complexIn[i] = new ComplexDouble(in[i], 0); } - - return out; + return ifft(complexIn); } /** diff --git a/src/test/java/org/apache/sysds/test/component/matrix/FourierTest.java b/src/test/java/org/apache/sysds/test/component/matrix/FourierTest.java index 99507206bcf..df1fd1fc3b5 100644 --- a/src/test/java/org/apache/sysds/test/component/matrix/FourierTest.java +++ b/src/test/java/org/apache/sysds/test/component/matrix/FourierTest.java @@ -17,6 +17,8 @@ public class FourierTest { + + // prior to executing this test it is necessary to run the Numpy Script in FourierTestData.py and add the generated file to the root of the project. @Test public void testFftWithNumpyData() throws IOException { String filename = "fft_data.csv"; // path to your CSV file @@ -55,6 +57,7 @@ public void testFftWithNumpyData() throws IOException { } + // prior to executing this test it is necessary to run the Numpy Script in FourierTestData.py and add the generated file to the root of the project. @Test public void testFftExecutionTime() throws IOException { String filename = "fft_data.csv"; // path to your CSV file @@ -81,13 +84,16 @@ public void testFftExecutionTime() throws IOException { long startTime = System.nanoTime(); fft(input); long endTime = System.nanoTime(); - totalTime += (endTime - startTime); - numCalculations++; + if(lineNumber > 1000){ + totalTime += (endTime - startTime); + numCalculations++; + - if (numCalculations % 1000 == 0) { - double averageTime = (totalTime / 1e6) / numCalculations; // Average time in milliseconds - System.out.println("\n\n\n\n\n\n\n\nAverage execution time after " + numCalculations + " calculations: " + averageTime + " ms \n\n\n\n\n\n\n\n"); + if (numCalculations % 5000 == 0) { + double averageTime = (totalTime / 1e6) / numCalculations; // Average time in milliseconds + System.out.println("\n\n\n\n\n\n\n\nAverage execution time after " + numCalculations + " calculations: " + averageTime + " ms \n\n\n\n\n\n\n\n"); + } } } @@ -230,6 +236,163 @@ public void testSimpleIfft1d() { } } + // prior to executing this test it is necessary to run the Numpy Script in FourierTestData.py and add the generated file to the root of the project. + @Test + public void testIfftWithRealNumpyData() throws IOException { + String filename = "ifft_data.csv"; // path to your IFFT data file + BufferedReader reader = new BufferedReader(new FileReader(filename)); + String line; + int lineNumber = 0; + long totalTime = 0; // Total time for all IFFT computations + int numCalculations = 0; // Number of IFFT computations + + while ((line = reader.readLine()) != null) { + lineNumber++; + String[] values = line.split(","); + int n = values.length / 3; + ComplexDouble[] originalInput = new ComplexDouble[n]; + ComplexDouble[] numpyIfftResult = new ComplexDouble[n]; + ComplexDouble[] javaIfftResult; + + for (int i = 0; i < n; i++) { + originalInput[i] = new ComplexDouble(Double.parseDouble(values[i]), 0); // Original data + double realPart = Double.parseDouble(values[n + i]); + double imagPart = Double.parseDouble(values[n * 2 + i]); + numpyIfftResult[i] = new ComplexDouble(realPart, imagPart); // NumPy IFFT result + } + + long startTime = System.nanoTime(); + javaIfftResult = ifft(originalInput); + long endTime = System.nanoTime(); + if(lineNumber > 1000){ + totalTime += (endTime - startTime); + numCalculations++; + } + for (int i = 0; i < n; i++) { + assertComplexEquals("Mismatch at index " + i + " in line " + lineNumber, numpyIfftResult[i], javaIfftResult[i]); + } + + if (numCalculations % 5000 == 0) { + double averageTime = (totalTime / 1e6) / numCalculations; // Average time in milliseconds + System.out.println("Average execution time after " + numCalculations + " calculations: " + averageTime + " ms"); + // System.out.println("input: "); + // for(int i = 0; i < originalInput.length; i++ ){ + // System.out.println(originalInput[i].toString()); + // } + // System.out.println("output: " ); + // for(int i = 0; i < javaIfftResult.length; i++ ){ + // System.out.println(javaIfftResult[i].toString()); + // } + } + } + + reader.close(); + System.out.println("Finished processing " + lineNumber + " lines."); + } + + @Test + public void testIfftWithComplexNumpyData() throws IOException { + String filename = "complex_ifft_data.csv"; // Adjusted path to your IFFT data file with complex inputs + BufferedReader reader = new BufferedReader(new FileReader(filename)); + String line; + int lineNumber = 0; + long totalTime = 0; // Total time for all IFFT computations + int numCalculations = 0; // Number of IFFT computations + + while ((line = reader.readLine()) != null) { + lineNumber++; + String[] values = line.split(","); + int n = values.length / 4; // Adjusted for complex numbers + ComplexDouble[] originalInput = new ComplexDouble[n]; + ComplexDouble[] numpyIfftResult = new ComplexDouble[n]; + ComplexDouble[] javaIfftResult; + + for (int i = 0; i < n; i++) { + double realPartOriginal = Double.parseDouble(values[i]); + double imagPartOriginal = Double.parseDouble(values[i + n]); + originalInput[i] = new ComplexDouble(realPartOriginal, imagPartOriginal); // Original complex data + + double realPartIfft = Double.parseDouble(values[i + 2 * n]); + double imagPartIfft = Double.parseDouble(values[i + 3 * n]); + numpyIfftResult[i] = new ComplexDouble(realPartIfft, imagPartIfft); // NumPy IFFT result + } + + long startTime = System.nanoTime(); + javaIfftResult = ifft(originalInput); + long endTime = System.nanoTime(); + + if(lineNumber > 1000){ + totalTime += (endTime - startTime); + numCalculations++; + } + for (int i = 0; i < n; i++) { + assertComplexEquals("Mismatch at index " + i + " in line " + lineNumber, numpyIfftResult[i], javaIfftResult[i]); + } + + if (numCalculations % 5000 == 0) { + double averageTime = (totalTime / 1e6) / numCalculations; // Average time in milliseconds + System.out.println("Average execution time after " + numCalculations + " calculations: " + averageTime + " ms"); + // System.out.println("input: "); + // for(int i = 0; i < originalInput.length; i++ ){ + // System.out.println(originalInput[i].toString()); + // } + // System.out.println("output: " ); + // for(int i = 0; i < javaIfftResult.length; i++ ){ + // System.out.println(javaIfftResult[i].toString()); + // } + + } + } + + reader.close(); + System.out.println("Finished processing " + lineNumber + " lines."); + } + + + // prior to executing this test it is necessary to run the Numpy Script in FourierTestData.py and add the generated file to the root of the project. + @Test + public void testIfftExecutionTime() throws IOException { + String filename = "ifft_data.csv"; // path to your IFFT data file + BufferedReader reader = new BufferedReader(new FileReader(filename)); + String line; + int lineNumber = 0; + long totalTime = 0; // Total time for all IFFT computations + int numCalculations = 0; // Number of IFFT computations + + while ((line = reader.readLine()) != null) { + lineNumber++; + String[] values = line.split(","); + int n = values.length / 3; + ComplexDouble[] original = new ComplexDouble[n]; + ComplexDouble[] numpyResult = new ComplexDouble[n]; + + for (int i = 0; i < n; i++) { + original[i] = new ComplexDouble(Double.parseDouble(values[i]), 0); + double realPart = Double.parseDouble(values[n + i]); + double imagPart = Double.parseDouble(values[n * 2 + i]); + numpyResult[i] = new ComplexDouble(realPart, imagPart); + } + + long startTime = System.nanoTime(); + ifft(numpyResult); + long endTime = System.nanoTime(); + if(lineNumber > 5000){ + totalTime += (endTime - startTime); + numCalculations++; + + + if (numCalculations % 1000 == 0) { + double averageTime = (totalTime / 1e6) / numCalculations; // Average time in milliseconds + System.out.println("Average execution time after " + numCalculations + " calculations: " + averageTime + " ms"); + } + } + } + + reader.close(); + System.out.println("Finished processing " + lineNumber + " lines."); + } + + @Test public void testSimpleIfft2d() { double[][] original = {{1, -2}, {3, -4}}; diff --git a/src/test/java/org/apache/sysds/test/component/matrix/FourierTestData.py b/src/test/java/org/apache/sysds/test/component/matrix/FourierTestData.py index 289995f3561..987a2157a81 100644 --- a/src/test/java/org/apache/sysds/test/component/matrix/FourierTestData.py +++ b/src/test/java/org/apache/sysds/test/component/matrix/FourierTestData.py @@ -8,6 +8,15 @@ def generate_inputs(num_inputs, max_power): length = 2 ** power yield np.random.rand(length) # generate array of random floats +def generate_complex_inputs(num_inputs, max_power): + for _ in range(num_inputs): + power = np.random.randint(1, max_power+1) + length = 2 ** power + real_part = np.random.rand(length) + imag_part = np.random.rand(length) + complex_array = real_part + 1j * imag_part + yield complex_array + def compute_fft(inputs): total_time = 0 num_calculations = 0 @@ -26,6 +35,24 @@ def compute_fft(inputs): yield result +def compute_ifft(inputs): + total_time = 0 + num_calculations = 0 + + for input_array in inputs: + start_time = time.time() + result = np.fft.ifft(input_array) + end_time = time.time() + + total_time += end_time - start_time + num_calculations += 1 + + if num_calculations % 1000 == 0: + average_time = total_time / num_calculations + print(f"Average execution time after {num_calculations} calculations: {average_time:.6f} seconds") + + yield result + def save_to_file(inputs, outputs, filename, mode='a'): with open(filename, mode, newline='') as file: writer = csv.writer(file) @@ -33,18 +60,46 @@ def save_to_file(inputs, outputs, filename, mode='a'): flattened_data = np.concatenate((input_array, output_array.real, output_array.imag)) writer.writerow(flattened_data) + +def save_to_file_complex(inputs, outputs, filename, mode='a'): + with open(filename, mode, newline='') as file: + writer = csv.writer(file) + for input_array, output_array in zip(inputs, outputs): + flattened_data = np.concatenate((input_array.real, input_array.imag, output_array.real, output_array.imag)) + writer.writerow(flattened_data) + # Parameters num_inputs = 100000 batch_size = 10000 max_power = 10 # example max power of 2 for input length -filename = "fft_data.csv" # Process and save in batches for i in range(0, num_inputs, batch_size): current_batch = min(batch_size, num_inputs - i) inputs = list(generate_inputs(current_batch, max_power)) outputs = list(compute_fft(inputs)) - save_to_file(inputs, outputs, filename, mode='a' if i > 0 else 'w') - print(f"Batch {i//batch_size + 1} out of {num_inputs//batch_size} saved to {filename}") + save_to_file(inputs, outputs, "fft_data.csv", mode='a' if i > 0 else 'w') + print(f"Batch {i//batch_size + 1} out of {num_inputs//batch_size} saved to fft_data.csv") + +print("All data for fft processed and saved.") + +# Process and save in batches +for i in range(0, num_inputs, batch_size): + current_batch = min(batch_size, num_inputs - i) + inputs = list(generate_inputs(current_batch, max_power)) + outputs = list(compute_ifft(inputs)) + save_to_file(inputs, outputs, "ifft_data.csv", mode='a' if i > 0 else 'w') + print(f"Batch {i//batch_size + 1} out of {num_inputs//batch_size} saved to ifft_data.csv") + +print("All real iff data processed and saved.") + +# Process and save in batches +for i in range(0, num_inputs, batch_size): + current_batch = min(batch_size, num_inputs - i) + inputs = list(generate_complex_inputs(current_batch, max_power)) + outputs = list(compute_ifft(inputs)) + save_to_file_complex(inputs, outputs, "complex_ifft_data.csv", mode='a' if i > 0 else 'w') + print(f"Batch {i//batch_size + 1} out of {num_inputs//batch_size} saved to complex_ifft_data.csv") + +print("All complex ifft data processed and saved.") -print("All data processed and saved.") \ No newline at end of file From b652af3bc7e668ccdf885ac9e215ff7dbb4b44a1 Mon Sep 17 00:00:00 2001 From: Frederic Zoepffel Date: Mon, 18 Dec 2023 15:08:45 +0100 Subject: [PATCH 011/133] added 2D ifft and tests --- .../runtime/matrix/data/LibMatrixFourier.java | 2 +- .../test/component/matrix/FourierTest.java | 69 +++++++++++++++++-- .../test/component/matrix/FourierTestData.py | 62 ++++++++++++++++- 3 files changed, 126 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java index 4162c26ca57..18c2066ed19 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java @@ -12,7 +12,7 @@ public class LibMatrixFourier { public static ComplexDouble[] fft(ComplexDouble[] in){ // TODO: how to invert fillToPowerOfTwo after calculation - // in = fillToPowerOfTwo(in); + // in = fillToPowerOfTwo(in); int n = in.length; if(n == 1){ diff --git a/src/test/java/org/apache/sysds/test/component/matrix/FourierTest.java b/src/test/java/org/apache/sysds/test/component/matrix/FourierTest.java index df1fd1fc3b5..26b0101a163 100644 --- a/src/test/java/org/apache/sysds/test/component/matrix/FourierTest.java +++ b/src/test/java/org/apache/sysds/test/component/matrix/FourierTest.java @@ -92,7 +92,7 @@ public void testFftExecutionTime() throws IOException { if (numCalculations % 5000 == 0) { double averageTime = (totalTime / 1e6) / numCalculations; // Average time in milliseconds - System.out.println("\n\n\n\n\n\n\n\nAverage execution time after " + numCalculations + " calculations: " + averageTime + " ms \n\n\n\n\n\n\n\n"); + System.out.println("\nAverage execution time after " + numCalculations + " calculations: " + String.format("%.8f", averageTime/1000) + " s \n"); } } } @@ -274,7 +274,7 @@ public void testIfftWithRealNumpyData() throws IOException { if (numCalculations % 5000 == 0) { double averageTime = (totalTime / 1e6) / numCalculations; // Average time in milliseconds - System.out.println("Average execution time after " + numCalculations + " calculations: " + averageTime + " ms"); + System.out.println("Average execution time after " + numCalculations + " calculations: " + String.format("%.8f", averageTime/1000) + " s"); // System.out.println("input: "); // for(int i = 0; i < originalInput.length; i++ ){ // System.out.println(originalInput[i].toString()); @@ -331,7 +331,7 @@ public void testIfftWithComplexNumpyData() throws IOException { if (numCalculations % 5000 == 0) { double averageTime = (totalTime / 1e6) / numCalculations; // Average time in milliseconds - System.out.println("Average execution time after " + numCalculations + " calculations: " + averageTime + " ms"); + System.out.println("Average execution time after " + numCalculations + " calculations: " + String.format("%.8f", averageTime/1000) + " s"); // System.out.println("input: "); // for(int i = 0; i < originalInput.length; i++ ){ // System.out.println(originalInput[i].toString()); @@ -383,7 +383,7 @@ public void testIfftExecutionTime() throws IOException { if (numCalculations % 1000 == 0) { double averageTime = (totalTime / 1e6) / numCalculations; // Average time in milliseconds - System.out.println("Average execution time after " + numCalculations + " calculations: " + averageTime + " ms"); + System.out.println("Average execution time after " + numCalculations + " calculations: " + String.format("%.8f", averageTime/1000) + " s"); } } } @@ -419,4 +419,63 @@ private static void assertEquals(String message, double expected, double actual, assertTrue(message + " - Expected: " + expected + ", Actual: " + actual, Math.abs(expected - actual) <= tolerance); } -} + @Test + public void testIfft2dWithNumpyData() throws IOException { + String filename = "complex_ifft_2d_data.csv"; // path to your 2D IFFT data file + BufferedReader reader = new BufferedReader(new FileReader(filename)); + String line; + int lineNumber = 0; + long totalTime = 0; // Total time for all IFFT 2D computations + int numCalculations = 0; // Number of IFFT 2D computations + int progressInterval = 10000; // Print progress every 1000 lines + + while ((line = reader.readLine()) != null) { + lineNumber++; + String[] values = line.split(","); + int halfLength = values.length / 4; + int sideLength = (int) Math.sqrt(halfLength); // Assuming square matrix + + ComplexDouble[][] originalInput = new ComplexDouble[sideLength][sideLength]; + ComplexDouble[][] numpyIfftResult = new ComplexDouble[sideLength][sideLength]; + + for (int i = 0; i < halfLength; i++) { + int row = i / sideLength; + int col = i % sideLength; + double realPartOriginal = Double.parseDouble(values[i]); + double imagPartOriginal = Double.parseDouble(values[i + halfLength]); + originalInput[row][col] = new ComplexDouble(realPartOriginal, imagPartOriginal); + + double realPartIfft = Double.parseDouble(values[i + 2 * halfLength]); + double imagPartIfft = Double.parseDouble(values[i + 3 * halfLength]); + numpyIfftResult[row][col] = new ComplexDouble(realPartIfft, imagPartIfft); + } + + long startTime = System.nanoTime(); + ComplexDouble[][] javaIfftResult = ifft2d(originalInput); + long endTime = System.nanoTime(); + if(lineNumber > 1000){ + totalTime += (endTime - startTime); + numCalculations++; + } + + for (int i = 0; i < sideLength; i++) { + for (int j = 0; j < sideLength; j++) { + assertComplexEquals("Mismatch at [" + i + "][" + j + "] in line " + lineNumber, numpyIfftResult[i][j], javaIfftResult[i][j]); + } + } + + if (lineNumber % progressInterval == 0) { // Print progress + System.out.println("Processing line: " + lineNumber); + if (numCalculations > 0) { + double averageTime = (totalTime / 1e6) / numCalculations; // Average time in milliseconds + System.out.println("Average execution time after " + numCalculations + " calculations: " + String.format("%.8f", averageTime/1000) + " s"); + } + } + } + + reader.close(); + System.out.println("Finished processing " + lineNumber + " lines. Average execution time: " + String.format("%.8f", (totalTime / 1e6 / numCalculations)/1000) + " ms"); + } + + +} \ No newline at end of file diff --git a/src/test/java/org/apache/sysds/test/component/matrix/FourierTestData.py b/src/test/java/org/apache/sysds/test/component/matrix/FourierTestData.py index 987a2157a81..23b38a44fdf 100644 --- a/src/test/java/org/apache/sysds/test/component/matrix/FourierTestData.py +++ b/src/test/java/org/apache/sysds/test/component/matrix/FourierTestData.py @@ -16,6 +16,7 @@ def generate_complex_inputs(num_inputs, max_power): imag_part = np.random.rand(length) complex_array = real_part + 1j * imag_part yield complex_array + def compute_fft(inputs): total_time = 0 @@ -52,6 +53,8 @@ def compute_ifft(inputs): print(f"Average execution time after {num_calculations} calculations: {average_time:.6f} seconds") yield result + + def save_to_file(inputs, outputs, filename, mode='a'): with open(filename, mode, newline='') as file: @@ -67,12 +70,51 @@ def save_to_file_complex(inputs, outputs, filename, mode='a'): for input_array, output_array in zip(inputs, outputs): flattened_data = np.concatenate((input_array.real, input_array.imag, output_array.real, output_array.imag)) writer.writerow(flattened_data) + +def generate_complex_inputs_2d(num_inputs, max_power): + for _ in range(num_inputs): + power = np.random.randint(1, max_power+1) + rows = 2 ** power + cols = 2 ** power + real_part = np.random.rand(rows, cols) + imag_part = np.random.rand(rows, cols) + complex_array = real_part + 1j * imag_part + yield complex_array + +def compute_ifft_2d(inputs): + + total_time = 0 + num_calculations = 0 + + for input_array in inputs: + start_time = time.time() + result = np.fft.ifft2(input_array) + end_time = time.time() + + total_time += end_time - start_time + num_calculations += 1 + + if num_calculations % 1000 == 0: + average_time = total_time / num_calculations + print(f"Average execution time after {num_calculations} calculations: {average_time:.6f} seconds") + + yield result + +def save_to_file_complex_2d(inputs, outputs, filename, mode='a'): + with open(filename, mode, newline='') as file: + writer = csv.writer(file) + for input_array, output_array in zip(inputs, outputs): + flattened_input = np.concatenate((input_array.real.flatten(), input_array.imag.flatten())) + flattened_output = np.concatenate((output_array.real.flatten(), output_array.imag.flatten())) + writer.writerow(np.concatenate((flattened_input, flattened_output))) + # Parameters num_inputs = 100000 batch_size = 10000 max_power = 10 # example max power of 2 for input length + # Process and save in batches for i in range(0, num_inputs, batch_size): current_batch = min(batch_size, num_inputs - i) @@ -83,7 +125,9 @@ def save_to_file_complex(inputs, outputs, filename, mode='a'): print("All data for fft processed and saved.") -# Process and save in batches +# Process and save in batches + + for i in range(0, num_inputs, batch_size): current_batch = min(batch_size, num_inputs - i) inputs = list(generate_inputs(current_batch, max_power)) @@ -103,3 +147,19 @@ def save_to_file_complex(inputs, outputs, filename, mode='a'): print("All complex ifft data processed and saved.") + +num_inputs = 100000 +batch_size = 1000 +max_power = 3 + +# Process and save 2D IFFT data in batches +filename_2d = "complex_ifft_2d_data.csv" +for i in range(0, num_inputs, batch_size): + current_batch = min(batch_size, num_inputs - i) + inputs_2d = list(generate_complex_inputs_2d(current_batch, max_power)) + outputs_2d = list(compute_ifft_2d(inputs_2d)) + save_to_file_complex_2d(inputs_2d, outputs_2d, filename_2d, mode='a' if i > 0 else 'w') + print(f"Batch {i//batch_size + 1} out of {num_inputs//batch_size} saved to {filename_2d}") + +print("All complex 2D ifft data processed and saved.") + From d7399ca6ba5be3d470b1daf6606229e9f3f5b345 Mon Sep 17 00:00:00 2001 From: Frederic Zoepffel Date: Mon, 18 Dec 2023 16:08:05 +0100 Subject: [PATCH 012/133] added test for 2dfft --- .../test/component/matrix/FourierTest.java | 58 ++++++++++++++++++- 1 file changed, 57 insertions(+), 1 deletion(-) diff --git a/src/test/java/org/apache/sysds/test/component/matrix/FourierTest.java b/src/test/java/org/apache/sysds/test/component/matrix/FourierTest.java index 26b0101a163..8d688d543c0 100644 --- a/src/test/java/org/apache/sysds/test/component/matrix/FourierTest.java +++ b/src/test/java/org/apache/sysds/test/component/matrix/FourierTest.java @@ -419,7 +419,63 @@ private static void assertEquals(String message, double expected, double actual, assertTrue(message + " - Expected: " + expected + ", Actual: " + actual, Math.abs(expected - actual) <= tolerance); } - @Test + @Test + public void testFft2dWithNumpyData() throws IOException { + String filename = "complex_fft_2d_data.csv"; // path to your 2D FFT data file + BufferedReader reader = new BufferedReader(new FileReader(filename)); + String line; + int lineNumber = 0; + long totalTime = 0; // Total time for all FFT 2D computations + int numCalculations = 0; // Number of FFT 2D computations + int progressInterval = 1000; // Print progress every 1000 lines + + while ((line = reader.readLine()) != null) { + lineNumber++; + String[] values = line.split(","); + int halfLength = values.length / 4; + int sideLength = (int) Math.sqrt(halfLength); // Assuming square matrix + + ComplexDouble[][] originalInput = new ComplexDouble[sideLength][sideLength]; + ComplexDouble[][] numpyFftResult = new ComplexDouble[sideLength][sideLength]; + + for (int i = 0; i < halfLength; i++) { + int row = i / sideLength; + int col = i % sideLength; + double realPartOriginal = Double.parseDouble(values[i]); + double imagPartOriginal = Double.parseDouble(values[i + halfLength]); + originalInput[row][col] = new ComplexDouble(realPartOriginal, imagPartOriginal); + + double realPartFft = Double.parseDouble(values[i + 2 * halfLength]); + double imagPartFft = Double.parseDouble(values[i + 3 * halfLength]); + numpyFftResult[row][col] = new ComplexDouble(realPartFft, imagPartFft); + } + + long startTime = System.nanoTime(); + ComplexDouble[][] javaFftResult = fft2d(originalInput); + long endTime = System.nanoTime(); + totalTime += (endTime - startTime); + numCalculations++; + + for (int i = 0; i < sideLength; i++) { + for (int j = 0; j < sideLength; j++) { + assertComplexEquals("Mismatch at [" + i + "][" + j + "] in line " + lineNumber, numpyFftResult[i][j], javaFftResult[i][j]); + } + } + + if (lineNumber % progressInterval == 0) { // Print progress + System.out.println("Processing line: " + lineNumber); + if (numCalculations > 0) { + double averageTime = (totalTime / 1e6) / numCalculations; // Average time in milliseconds + System.out.println("Average execution time after " + numCalculations + " calculations: " + String.format("%.8f", averageTime/1000) + " s"); + } + } + } + + reader.close(); + System.out.println("Finished processing " + lineNumber + " lines. Average execution time: " + String.format("%.8f", (totalTime / 1e6 / numCalculations)/1000) + " s"); + } + + @Test public void testIfft2dWithNumpyData() throws IOException { String filename = "complex_ifft_2d_data.csv"; // path to your 2D IFFT data file BufferedReader reader = new BufferedReader(new FileReader(filename)); From f4b1207397e83b18ccc293807a14e4548a4a4a55 Mon Sep 17 00:00:00 2001 From: Frederic Zoepffel Date: Mon, 18 Dec 2023 17:09:57 +0100 Subject: [PATCH 013/133] added dml builtin stuff --- .../org/apache/sysds/common/Builtins.java | 1 + .../parser/BuiltinFunctionExpression.java | 28 +++++++++++++++++++ 2 files changed, 29 insertions(+) diff --git a/src/main/java/org/apache/sysds/common/Builtins.java b/src/main/java/org/apache/sysds/common/Builtins.java index f8377ce1edc..9366acebf72 100644 --- a/src/main/java/org/apache/sysds/common/Builtins.java +++ b/src/main/java/org/apache/sysds/common/Builtins.java @@ -133,6 +133,7 @@ public enum Builtins { FIT_PIPELINE("fit_pipeline", true), FIX_INVALID_LENGTHS("fixInvalidLengths", true), FIX_INVALID_LENGTHS_APPLY("fixInvalidLengthsApply", true), + FFT("fft", false, ReturnType.MULTI_RETURN), FF_TRAIN("ffTrain", true), FF_PREDICT("ffPredict", true), FLOOR("floor", false), diff --git a/src/main/java/org/apache/sysds/parser/BuiltinFunctionExpression.java b/src/main/java/org/apache/sysds/parser/BuiltinFunctionExpression.java index fa0354843b8..7c682175996 100644 --- a/src/main/java/org/apache/sysds/parser/BuiltinFunctionExpression.java +++ b/src/main/java/org/apache/sysds/parser/BuiltinFunctionExpression.java @@ -355,6 +355,34 @@ public void validateExpression(MultiAssignmentStatement stmt, HashMap Date: Mon, 18 Dec 2023 17:37:27 +0100 Subject: [PATCH 014/133] removed complexDouble.pow --- .../sysds/runtime/matrix/data/ComplexDouble.java | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/ComplexDouble.java b/src/main/java/org/apache/sysds/runtime/matrix/data/ComplexDouble.java index 7fd09d3f942..668c61690b8 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/ComplexDouble.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/ComplexDouble.java @@ -20,21 +20,6 @@ public ComplexDouble mul(ComplexDouble other){ return new ComplexDouble(this.re * other.re - this.im * other.im, this.im * other.re + this.re * other.im); } - /** - * Powering of a complex double. - * First, the polar form is calculated and then De Moivre's theorem is applied. - * - * @param n exponent - * @return the n-th power of the complex double - */ - public ComplexDouble pow(int n){ - double dist = Math.sqrt(this.re * this.re + this.im * this.im); - double angle = Math.atan2(this.im, this.re); - - return new ComplexDouble(Math.pow(dist, n) * Math.cos(n * angle), - Math.pow(dist, n) * Math.sin(n * angle)); - } - // Division public ComplexDouble div(ComplexDouble other) { double denominator = other.re * other.re + other.im * other.im; From bb79afe2f54732033db9285280ae540ea306b2c0 Mon Sep 17 00:00:00 2001 From: psej Date: Mon, 18 Dec 2023 20:01:28 +0100 Subject: [PATCH 015/133] added fft matrixblock 1 dim --- .../runtime/matrix/data/LibMatrixFourier.java | 83 +++++++++++++++++++ .../test/component/matrix/FourierTest.java | 31 ++++++- 2 files changed, 110 insertions(+), 4 deletions(-) diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java index 18c2066ed19..c357d6a2233 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java @@ -2,6 +2,89 @@ public class LibMatrixFourier { + /** + * Function to perform Fast Fourier Transformation on a given array. + * Its length has to be a power of two. + * + * @param in array of doubles + * @return array of ComplexDoubles + */ + public static MatrixBlock[] fft_new(double[] in){ + + int cols = in.length; + MatrixBlock re = new MatrixBlock(1, cols, false); + re.init(in, 1, cols); + + return fft(re); + } + + public static MatrixBlock[] fft(MatrixBlock re){ + + int rows = re.getNumRows(); + int cols = re.getNumColumns(); + if(!isPowerOfTwo(cols)) throw new RuntimeException("dimension is not power of two"); + if(rows != 1) throw new RuntimeException("not yet implemented for more dimensions"); + + if(cols == 1){ + // generate new MatrixBlock of same dimensions with 0s + MatrixBlock im = new MatrixBlock(1, cols, new double[cols]); + return new MatrixBlock[]{re, im}; + } + + // get values of first row + double[] values = re.getDenseBlockValues(); + + // split values depending on index + double[] even = new double[cols/2]; + double[] odd = new double[cols/2]; + for(int i = 0; i < cols/2; i++){ + even[i] = values[i*2]; + odd[i] = values[i*2+1]; + } + + MatrixBlock[] res_even = fft(new MatrixBlock(1, cols/2, even)); + MatrixBlock[] res_odd = fft(new MatrixBlock(1, cols/2, odd)); + + double[][] res_even_values = new double[][]{ + res_even[0].getDenseBlockValues(), + res_even[1].getDenseBlockValues()}; + + double[][] res_odd_values = new double[][]{ + res_odd[0].getDenseBlockValues(), + res_odd[1].getDenseBlockValues()}; + + double angle = -2*Math.PI/cols; + double[][] res = new double[2][cols]; + + for(int j=0; j < cols/2; j++){ + + double[] omega_pow = new double[]{ Math.cos(j*angle), Math.sin(j*angle)}; + + // m = omega * res_odd[j] + double[] m = new double[]{ + omega_pow[0] * res_odd_values[0][j] - omega_pow[1] * res_odd_values[1][j], + omega_pow[0] * res_odd_values[1][j] + omega_pow[1] * res_odd_values[0][j]}; + + // res[j] = res_even + m; + res[0][j] = res_even_values[0][j] + m[0]; + res[1][j] = res_even_values[1][j] + m[1]; + + // res[j+cols/2] = res_even - m; + res[0][j+cols/2] = res_even_values[0][j] - m[0]; + res[1][j+cols/2] = res_even_values[1][j] - m[1]; + + } + + MatrixBlock res_re = new MatrixBlock(rows, cols, res[0]); + MatrixBlock res_im = new MatrixBlock(rows, cols, res[1]); + + return new MatrixBlock[]{res_re, res_im}; + } + + private static boolean isPowerOfTwo(int n){ + return (n != 0) && ((n & (n - 1)) == 0); + } + /** * Function to perform Fast Fourier Transformation on a given array. * Its length has to be a power of two. diff --git a/src/test/java/org/apache/sysds/test/component/matrix/FourierTest.java b/src/test/java/org/apache/sysds/test/component/matrix/FourierTest.java index 8d688d543c0..7e75d5316a9 100644 --- a/src/test/java/org/apache/sysds/test/component/matrix/FourierTest.java +++ b/src/test/java/org/apache/sysds/test/component/matrix/FourierTest.java @@ -1,13 +1,11 @@ package org.apache.sysds.test.component.matrix; import org.apache.sysds.runtime.matrix.data.ComplexDouble; -import static org.apache.sysds.runtime.matrix.data.LibMatrixFourier.fft; -import static org.apache.sysds.runtime.matrix.data.LibMatrixFourier.fft2d; -import static org.apache.sysds.runtime.matrix.data.LibMatrixFourier.ifft; -import static org.apache.sysds.runtime.matrix.data.LibMatrixFourier.ifft2d; +import org.apache.sysds.runtime.matrix.data.MatrixBlock; import org.junit.Test; +import static org.apache.sysds.runtime.matrix.data.LibMatrixFourier.*; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertTrue; @@ -136,6 +134,31 @@ public void simpleTest() { assertArrayEquals(expected, res); } + @Test + public void new_test(){ + + double[] in = {0, 18, -15, 3}; + + double[] expected_re = {6,15,-36,15}; + double[] expected_im = {0,-15,0,15}; + + MatrixBlock[] res = fft_new(in); + double[] res_re = res[0].getDenseBlockValues(); + double[] res_im = res[1].getDenseBlockValues(); + + for(double elem : res_re){ + System.out.print(elem+" "); + } + System.out.println(); + for(double elem : res_im){ + System.out.print(elem+" "); + } + + assertArrayEquals(expected_re, res_re, 0.0001); + assertArrayEquals(expected_im, res_im, 0.0001); + } + + @Test public void notPowerOfTwoTest() { double[] in = {1, 2, 3, 4, 5}; From f79d0055e5ad07d2c556f64794bad1598e63153a Mon Sep 17 00:00:00 2001 From: mufwan Date: Mon, 18 Dec 2023 23:04:14 +0100 Subject: [PATCH 016/133] fft for arrays with non-power-of-two length --- .../matrix/data/LibMatrixFourier2.java | 190 ++++++++++++++++++ .../test/component/matrix/Fourier2Test.java | 58 ++++++ 2 files changed, 248 insertions(+) create mode 100644 src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier2.java create mode 100644 src/test/java/org/apache/sysds/test/component/matrix/Fourier2Test.java diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier2.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier2.java new file mode 100644 index 00000000000..8f347ad4244 --- /dev/null +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier2.java @@ -0,0 +1,190 @@ +package org.apache.sysds.runtime.matrix.data; + +public class LibMatrixFourier2 { + + + public static ComplexDouble[] fft(double[] in){ + ComplexDouble[] complex = new ComplexDouble[in.length]; + double[] im = new double[in.length]; + transform(in, im); + for(int i=0; i>> (32 - levels); + if (j > i) { + double temp = real[i]; + real[i] = real[j]; + real[j] = temp; + temp = imag[i]; + imag[i] = imag[j]; + imag[j] = temp; + } + } + + // Cooley-Tukey decimation-in-time radix-2 FFT + for (int size = 2; size <= n; size *= 2) { + int halfsize = size / 2; + int tablestep = n / size; + for (int i = 0; i < n; i += size) { + for (int j = i, k = 0; j < i + halfsize; j++, k += tablestep) { + int l = j + halfsize; + double tpre = real[l] * cosTable[k] + imag[l] * sinTable[k]; + double tpim = -real[l] * sinTable[k] + imag[l] * cosTable[k]; + real[l] = real[j] - tpre; + imag[l] = imag[j] - tpim; + real[j] += tpre; + imag[j] += tpim; + } + } + if (size == n) // Prevent overflow in 'size *= 2' + break; + } + } + + + /* + * Computes the discrete Fourier transform (DFT) of the given complex vector, storing the result back into the vector. + * The vector can have any length. This requires the convolution function, which in turn requires the radix-2 FFT function. + * Uses Bluestein's chirp z-transform algorithm. + */ + public static void transformBluestein(double[] real, double[] imag) { + // Find a power-of-2 convolution length m such that m >= n * 2 + 1 + int n = real.length; + if (n != imag.length) + throw new IllegalArgumentException("Mismatched lengths"); + if (n >= 0x20000000) + throw new IllegalArgumentException("Array too large"); + int m = Integer.highestOneBit(n) * 4; + + // Trigonometric tables + double[] cosTable = new double[n]; + double[] sinTable = new double[n]; + for (int i = 0; i < n; i++) { + int j = (int)((long)i * i % (n * 2)); // This is more accurate than j = i * i + cosTable[i] = Math.cos(Math.PI * j / n); + sinTable[i] = Math.sin(Math.PI * j / n); + } + + // Temporary vectors and preprocessing + double[] areal = new double[m]; + double[] aimag = new double[m]; + for (int i = 0; i < n; i++) { + areal[i] = real[i] * cosTable[i] + imag[i] * sinTable[i]; + aimag[i] = -real[i] * sinTable[i] + imag[i] * cosTable[i]; + } + double[] breal = new double[m]; + double[] bimag = new double[m]; + breal[0] = cosTable[0]; + bimag[0] = sinTable[0]; + for (int i = 1; i < n; i++) { + breal[i] = breal[m - i] = cosTable[i]; + bimag[i] = bimag[m - i] = sinTable[i]; + } + + // Convolution + double[] creal = new double[m]; + double[] cimag = new double[m]; + convolve(areal, aimag, breal, bimag, creal, cimag); + + // Postprocessing + for (int i = 0; i < n; i++) { + real[i] = creal[i] * cosTable[i] + cimag[i] * sinTable[i]; + imag[i] = -creal[i] * sinTable[i] + cimag[i] * cosTable[i]; + } + } + + + /* + * Computes the circular convolution of the given real vectors. Each vector's length must be the same. + */ + public static void convolve(double[] xvec, double[] yvec, double[] outvec) { + int n = xvec.length; + if (n != yvec.length || n != outvec.length) + throw new IllegalArgumentException("Mismatched lengths"); + convolve(xvec, new double[n], yvec, new double[n], outvec, new double[n]); + } + + + /* + * Computes the circular convolution of the given complex vectors. Each vector's length must be the same. + */ + public static void convolve(double[] xreal, double[] ximag, + double[] yreal, double[] yimag, double[] outreal, double[] outimag) { + + int n = xreal.length; + if (n != ximag.length || n != yreal.length || n != yimag.length + || n != outreal.length || n != outimag.length) + throw new IllegalArgumentException("Mismatched lengths"); + + xreal = xreal.clone(); + ximag = ximag.clone(); + yreal = yreal.clone(); + yimag = yimag.clone(); + transform(xreal, ximag); + transform(yreal, yimag); + + for (int i = 0; i < n; i++) { + double temp = xreal[i] * yreal[i] - ximag[i] * yimag[i]; + ximag[i] = ximag[i] * yreal[i] + xreal[i] * yimag[i]; + xreal[i] = temp; + } + inverseTransform(xreal, ximag); + + for (int i = 0; i < n; i++) { // Scaling (because this FFT implementation omits it) + outreal[i] = xreal[i] / n; + outimag[i] = ximag[i] / n; + } + } + +} diff --git a/src/test/java/org/apache/sysds/test/component/matrix/Fourier2Test.java b/src/test/java/org/apache/sysds/test/component/matrix/Fourier2Test.java new file mode 100644 index 00000000000..2df6e2c23fe --- /dev/null +++ b/src/test/java/org/apache/sysds/test/component/matrix/Fourier2Test.java @@ -0,0 +1,58 @@ +package org.apache.sysds.test.component.matrix; + +import org.apache.sysds.runtime.matrix.data.ComplexDouble; +import org.apache.sysds.runtime.matrix.data.LibMatrixFourier; +import org.apache.sysds.runtime.matrix.data.LibMatrixFourier2; + +import static org.apache.sysds.runtime.matrix.data.LibMatrixFourier2.fft; +import static org.apache.sysds.runtime.matrix.data.LibMatrixFourier2.transform; + +import org.junit.Test; + +import java.util.Arrays; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertTrue; + + +public class Fourier2Test { + + @Test + public void simpleTest() { + // tested with numpy + double[] in = {0, 18, -15, 3}; + ComplexDouble[] expected = new ComplexDouble[4]; + expected[0] = new ComplexDouble(6, 0); + expected[1] = new ComplexDouble(15, -15); + expected[2] = new ComplexDouble(-36, 0); + expected[3] = new ComplexDouble(15, 15); + + ComplexDouble[] res = fft(in); + for(ComplexDouble elem : res){ + System.out.println(elem); + } + + assertArrayEquals(expected, res); + } + + @Test + public void notPowerOfTwoTest() { + double[] in = {1, 2, 3, 4, 5}; + + // see https://de.mathworks.com/help/matlab/ref/ifft.html + ComplexDouble[] expected = new ComplexDouble[5]; + expected[0] = new ComplexDouble(15,0); + expected[1] = new ComplexDouble(-2.5000,3.4410); + expected[2] = new ComplexDouble(-2.5000,0.8123); + expected[3] = new ComplexDouble(-2.5000, - 0.8123); + expected[4] = new ComplexDouble(-2.5000, - 3.4410); + + ComplexDouble[] res = fft(in); + for(ComplexDouble elem : res){ + System.out.println(elem); + } + assertArrayEquals(expected, res); + } + + +} From b5a908f76e8ab532ca052c372747f864613b4da1 Mon Sep 17 00:00:00 2001 From: psej Date: Tue, 19 Dec 2023 01:18:41 +0100 Subject: [PATCH 017/133] fft matrixblock now supports both dim --- .../runtime/matrix/data/LibMatrixFourier.java | 113 +++++++++++++----- .../test/component/matrix/FourierTest.java | 60 +++++++++- 2 files changed, 138 insertions(+), 35 deletions(-) diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java index c357d6a2233..527fe1c9045 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java @@ -1,5 +1,7 @@ package org.apache.sysds.runtime.matrix.data; +import java.util.Arrays; + public class LibMatrixFourier { /** @@ -9,11 +11,22 @@ public class LibMatrixFourier { * @param in array of doubles * @return array of ComplexDoubles */ - public static MatrixBlock[] fft_new(double[] in){ + public static MatrixBlock[] fft(double[] in){ int cols = in.length; - MatrixBlock re = new MatrixBlock(1, cols, false); - re.init(in, 1, cols); + MatrixBlock re = new MatrixBlock(1, cols, in); + MatrixBlock im = new MatrixBlock(1, cols, new double[cols]); + + return fft_one_dim(re, im); + } + + public static MatrixBlock[] fft(double[][] in){ + + int cols = in[0].length; + int rows = in.length; + + double[] flattened = Arrays.stream(in).flatMapToDouble(Arrays::stream).toArray(); + MatrixBlock re = new MatrixBlock(rows, cols, flattened); return fft(re); } @@ -22,28 +35,75 @@ public static MatrixBlock[] fft(MatrixBlock re){ int rows = re.getNumRows(); int cols = re.getNumColumns(); + if(!isPowerOfTwo(cols)) throw new RuntimeException("dimension is not power of two"); - if(rows != 1) throw new RuntimeException("not yet implemented for more dimensions"); + + MatrixBlock[][] res_rows = new MatrixBlock[rows][2]; + + for(int i = 0; i < rows; i++){ + // use fft on each row + double[] row_values = Arrays.copyOfRange(re.getDenseBlockValues(), i * cols, (i+1) * cols); + res_rows[i] = fft_one_dim(new MatrixBlock(1, cols, row_values), new MatrixBlock(1, cols, new double[cols])); + } + + if(rows == 1) return res_rows[0]; + + double[][] res = new double[2][rows*cols]; + + // flatten res_row + for(int i = 0; i < rows; i++){ + double[] res_rows_re = res_rows[i][0].getDenseBlockValues(); + double[] res_rows_im = res_rows[i][1].getDenseBlockValues(); + for(int j = 0; j < cols; j++){ + res[0][i*cols+j] = res_rows_re[j]; + res[1][i*cols+j] = res_rows_im[j]; + } + } + + for(int j = 0; j < cols; j++) { + // double[re/im][] col_values + double[][] col_values = new double[2][rows]; + for (int i = 0; i < rows; i++) { + col_values[0][i] = res[0][i*cols+j]; + col_values[1][i] = res[1][i*cols+j]; + } + + MatrixBlock[] res_col = fft_one_dim(new MatrixBlock(1, rows, col_values[0]), new MatrixBlock(1, rows, col_values[1])); + for (int i = 0; i < rows; i++) { + res[0][i*cols+j] = res_col[0].getDenseBlockValues()[i]; + res[1][i*cols+j] = res_col[1].getDenseBlockValues()[i]; + } + } + + return new MatrixBlock[]{new MatrixBlock(rows, cols, res[0]), new MatrixBlock(rows, cols, res[1])}; + + } + + private static MatrixBlock[] fft_one_dim(MatrixBlock re, MatrixBlock im){ + + int rows = re.getNumRows(); + int cols = re.getNumColumns(); + if(rows != 1) throw new RuntimeException("only for one dimension"); if(cols == 1){ - // generate new MatrixBlock of same dimensions with 0s - MatrixBlock im = new MatrixBlock(1, cols, new double[cols]); return new MatrixBlock[]{re, im}; } - // get values of first row - double[] values = re.getDenseBlockValues(); + // 1st row real part, 2nd row imaginary part + double[][] values = {re.getDenseBlockValues(), im.getDenseBlockValues()}; // split values depending on index - double[] even = new double[cols/2]; - double[] odd = new double[cols/2]; - for(int i = 0; i < cols/2; i++){ - even[i] = values[i*2]; - odd[i] = values[i*2+1]; + double[][] even = new double[2][cols/2]; + double[][] odd = new double[2][cols/2]; + for(int j = 0; j < cols/2; j++){ + for(int i = 0; i < 2; i++){ + even[i][j] = values[i][j*2]; + odd[i][j] = values[i][j*2+1]; + } } - MatrixBlock[] res_even = fft(new MatrixBlock(1, cols/2, even)); - MatrixBlock[] res_odd = fft(new MatrixBlock(1, cols/2, odd)); + MatrixBlock[] res_even = fft_one_dim(new MatrixBlock(1, cols/2, even[0]), new MatrixBlock(1, cols/2, even[1])); + MatrixBlock[] res_odd = fft_one_dim(new MatrixBlock(1, cols/2, odd[0]), new MatrixBlock(1, cols/2, odd[1])); double[][] res_even_values = new double[][]{ res_even[0].getDenseBlockValues(), @@ -56,9 +116,9 @@ public static MatrixBlock[] fft(MatrixBlock re){ double angle = -2*Math.PI/cols; double[][] res = new double[2][cols]; - for(int j=0; j < cols/2; j++){ + for(int j = 0; j < cols/2; j++){ - double[] omega_pow = new double[]{ Math.cos(j*angle), Math.sin(j*angle)}; + double[] omega_pow = new double[]{Math.cos(j*angle), Math.sin(j*angle)}; // m = omega * res_odd[j] double[] m = new double[]{ @@ -66,23 +126,18 @@ public static MatrixBlock[] fft(MatrixBlock re){ omega_pow[0] * res_odd_values[1][j] + omega_pow[1] * res_odd_values[0][j]}; // res[j] = res_even + m; - res[0][j] = res_even_values[0][j] + m[0]; - res[1][j] = res_even_values[1][j] + m[1]; - // res[j+cols/2] = res_even - m; - res[0][j+cols/2] = res_even_values[0][j] - m[0]; - res[1][j+cols/2] = res_even_values[1][j] - m[1]; - + for(int i = 0; i < 2; i++){ + res[i][j] = res_even_values[i][j] + m[i]; + res[i][j+cols/2] = res_even_values[i][j] - m[i]; + } } - MatrixBlock res_re = new MatrixBlock(rows, cols, res[0]); - MatrixBlock res_im = new MatrixBlock(rows, cols, res[1]); - - return new MatrixBlock[]{res_re, res_im}; + return new MatrixBlock[]{new MatrixBlock(rows, cols, res[0]), new MatrixBlock(rows, cols, res[1])}; } private static boolean isPowerOfTwo(int n){ - return (n != 0) && ((n & (n - 1)) == 0); + return ((n != 0) && ((n & (n - 1)) == 0)) || n == 1; } /** @@ -131,7 +186,7 @@ public static ComplexDouble[] fft(ComplexDouble[] in){ * @param in array of doubles * @return array of ComplexDoubles */ - public static ComplexDouble[] fft(double[] in){ + public static ComplexDouble[] fft_old(double[] in){ ComplexDouble[] complex = new ComplexDouble[in.length]; for(int i=0; i 1000){ totalTime += (endTime - startTime); @@ -126,7 +126,7 @@ public void simpleTest() { expected[2] = new ComplexDouble(-36, 0); expected[3] = new ComplexDouble(15, 15); - ComplexDouble[] res = fft(in); + ComplexDouble[] res = fft_old(in); for(ComplexDouble elem : res){ System.out.println(elem); } @@ -135,14 +135,14 @@ public void simpleTest() { } @Test - public void new_test(){ + public void matrix_block_one_dim_test(){ double[] in = {0, 18, -15, 3}; double[] expected_re = {6,15,-36,15}; double[] expected_im = {0,-15,0,15}; - MatrixBlock[] res = fft_new(in); + MatrixBlock[] res = fft(in); double[] res_re = res[0].getDenseBlockValues(); double[] res_im = res[1].getDenseBlockValues(); @@ -158,6 +158,54 @@ public void new_test(){ assertArrayEquals(expected_im, res_im, 0.0001); } + @Test + public void matrix_block_one_dim_as_two_dim_test(){ + + double[][] in = {{0, 18, -15, 3}}; + + double[] expected_re = {6,15,-36,15}; + double[] expected_im = {0,-15,0,15}; + + MatrixBlock[] res = fft(in); + double[] res_re = res[0].getDenseBlockValues(); + double[] res_im = res[1].getDenseBlockValues(); + + for(double elem : res_re){ + System.out.print(elem+" "); + } + System.out.println(); + for(double elem : res_im){ + System.out.print(elem+" "); + } + + assertArrayEquals(expected_re, res_re, 0.0001); + assertArrayEquals(expected_im, res_im, 0.0001); + } + + @Test + public void matrix_block_two_dim_test(){ + + double[][] in = {{0, 18}, {-15, 3}}; + + double[] flattened_expected_re = {6,-36, 30,0}; + double[] flattened_expected_im = {0,0,0,0}; + + MatrixBlock[] res = fft(in); + double[] res_re = res[0].getDenseBlockValues(); + double[] res_im = res[1].getDenseBlockValues(); + + for(double elem : res_re){ + System.out.print(elem+" "); + } + System.out.println(); + for(double elem : res_im){ + System.out.print(elem+" "); + } + + assertArrayEquals(flattened_expected_re, res_re, 0.0001); + assertArrayEquals(flattened_expected_im, res_im, 0.0001); + } + @Test public void notPowerOfTwoTest() { @@ -171,7 +219,7 @@ public void notPowerOfTwoTest() { expected[3] = new ComplexDouble(-2.5000, - 0.8123); expected[4] = new ComplexDouble(-2.5000, - 3.4410); - ComplexDouble[] res = fft(in); + ComplexDouble[] res = fft_old(in); for(ComplexDouble elem : res){ System.out.println(elem); } From ddcdec9d8ce7d33751c95623faf9afc8c8f06189 Mon Sep 17 00:00:00 2001 From: psej Date: Tue, 19 Dec 2023 01:45:50 +0100 Subject: [PATCH 018/133] added ifft_one_dim MatrixBlock --- .../runtime/matrix/data/LibMatrixFourier.java | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java index 527fe1c9045..951f11aec43 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java @@ -194,6 +194,23 @@ public static ComplexDouble[] fft_old(double[] in){ return fft(complex); } + public static MatrixBlock[] ifft_one_dim(MatrixBlock re, MatrixBlock im){ + + int rows = re.getNumRows(); + int cols = re.getNumColumns(); + + if(rows != 1) throw new RuntimeException("only for one dimension"); + + double[] im_values = im.getDenseBlockValues(); + MatrixBlock conj = new MatrixBlock(rows, cols, Arrays.stream(im_values).map(i -> -i).toArray()); + + MatrixBlock[] res = fft_one_dim(re, conj); + + double[] res_re = Arrays.stream(res[0].getDenseBlockValues()).map(i -> i/cols).toArray(); + double[] res_im = Arrays.stream(res[0].getDenseBlockValues()).map(i -> -i/cols).toArray(); + + return new MatrixBlock[]{ new MatrixBlock(rows, cols, res_re), new MatrixBlock(rows, cols, res_im)}; + } public static ComplexDouble[] ifft(ComplexDouble[] in) { int n = in.length; From 9478200e709e5e31ad4b95c17a48eebdcb9152c0 Mon Sep 17 00:00:00 2001 From: psej Date: Thu, 21 Dec 2023 00:56:53 +0100 Subject: [PATCH 019/133] improved all fft MatrixBlock functions + separated from complex double funtions --- .../runtime/matrix/data/LibMatrixFourier.java | 412 +++++-------- .../data/LibMatrixFourierComplexDouble.java | 220 +++++++ .../matrix/FourierComplexDoubleTest.java | 536 +++++++++++++++++ .../test/component/matrix/FourierTest.java | 568 ++---------------- 4 files changed, 936 insertions(+), 800 deletions(-) create mode 100644 src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourierComplexDouble.java create mode 100644 src/test/java/org/apache/sysds/test/component/matrix/FourierComplexDoubleTest.java diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java index 951f11aec43..94a5523d1ba 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java @@ -5,371 +5,239 @@ public class LibMatrixFourier { /** - * Function to perform Fast Fourier Transformation on a given array. - * Its length has to be a power of two. - * - * @param in array of doubles - * @return array of ComplexDoubles + * Function to perform Fast Fourier Transformation */ - public static MatrixBlock[] fft(double[] in){ - - int cols = in.length; - MatrixBlock re = new MatrixBlock(1, cols, in); - MatrixBlock im = new MatrixBlock(1, cols, new double[cols]); - return fft_one_dim(re, im); - } + public static MatrixBlock[] fft(MatrixBlock re, MatrixBlock im){ - public static MatrixBlock[] fft(double[][] in){ + int rows = re.getNumRows(); + int cols = re.getNumColumns(); - int cols = in[0].length; - int rows = in.length; + double[][][] in = new double[2][rows][cols]; + in[0] = convertToArray(re); + in[1] = convertToArray(im); - double[] flattened = Arrays.stream(in).flatMapToDouble(Arrays::stream).toArray(); - MatrixBlock re = new MatrixBlock(rows, cols, flattened); + double[][][] res = fft(in, false); - return fft(re); + return convertToMatrixBlocks(res); } - public static MatrixBlock[] fft(MatrixBlock re){ + public static MatrixBlock[] ifft(MatrixBlock re, MatrixBlock im){ int rows = re.getNumRows(); int cols = re.getNumColumns(); - if(!isPowerOfTwo(cols)) throw new RuntimeException("dimension is not power of two"); + double[][][] in = new double[2][rows][cols]; + in[0] = convertToArray(re); + in[1] = convertToArray(im); - MatrixBlock[][] res_rows = new MatrixBlock[rows][2]; + double[][][] res = fft(in, true); - for(int i = 0; i < rows; i++){ - // use fft on each row - double[] row_values = Arrays.copyOfRange(re.getDenseBlockValues(), i * cols, (i+1) * cols); - res_rows[i] = fft_one_dim(new MatrixBlock(1, cols, row_values), new MatrixBlock(1, cols, new double[cols])); - } + return convertToMatrixBlocks(res); + } - if(rows == 1) return res_rows[0]; + public static double[][][] fft(double[][][] in, boolean calcInv){ - double[][] res = new double[2][rows*cols]; + int rows = in[0].length; + int cols = in[0][0].length; + + double[][][] res = new double[2][rows][cols]; - // flatten res_row for(int i = 0; i < rows; i++){ - double[] res_rows_re = res_rows[i][0].getDenseBlockValues(); - double[] res_rows_im = res_rows[i][1].getDenseBlockValues(); - for(int j = 0; j < cols; j++){ - res[0][i*cols+j] = res_rows_re[j]; - res[1][i*cols+j] = res_rows_im[j]; + // use fft or ifft on each row + double[][] res_row; + if(calcInv){ + res_row = ifft_one_dim(get_complex_row(in, i)); + } else { + res_row = fft_one_dim(get_complex_row(in, i)); + } + // set res row + for (int j = 0; j < cols; j++){ + for( int k = 0; k < 2; k++){ + res[k][i][j] = res_row[k][j]; + } } } - for(int j = 0; j < cols; j++) { - // double[re/im][] col_values - double[][] col_values = new double[2][rows]; - for (int i = 0; i < rows; i++) { - col_values[0][i] = res[0][i*cols+j]; - col_values[1][i] = res[1][i*cols+j]; - } + if(rows == 1) return res; - MatrixBlock[] res_col = fft_one_dim(new MatrixBlock(1, rows, col_values[0]), new MatrixBlock(1, rows, col_values[1])); - for (int i = 0; i < rows; i++) { - res[0][i*cols+j] = res_col[0].getDenseBlockValues()[i]; - res[1][i*cols+j] = res_col[1].getDenseBlockValues()[i]; + for(int j = 0; j < cols; j++){ + // use fft on each col + double[][] res_col; + if(calcInv){ + res_col = ifft_one_dim(get_complex_col(res, j)); + } else { + res_col = fft_one_dim(get_complex_col(res, j)); + } + // set res col + for (int i = 0; i < rows; i++){ + for( int k = 0; k < 2; k++){ + res[k][i][j] = res_col[k][i]; + } } } - return new MatrixBlock[]{new MatrixBlock(rows, cols, res[0]), new MatrixBlock(rows, cols, res[1])}; - + return res; } - private static MatrixBlock[] fft_one_dim(MatrixBlock re, MatrixBlock im){ - - int rows = re.getNumRows(); - int cols = re.getNumColumns(); - if(rows != 1) throw new RuntimeException("only for one dimension"); + public static double[][] fft_one_dim(double[][] in){ + // 1st row real part, 2nd row imaginary part + if(in == null || in.length != 2 || in[0].length != in[1].length) throw new RuntimeException("in false dimensions"); - if(cols == 1){ - return new MatrixBlock[]{re, im}; - } + int cols = in[0].length; + if(cols == 1) return in; - // 1st row real part, 2nd row imaginary part - double[][] values = {re.getDenseBlockValues(), im.getDenseBlockValues()}; + double angle = -2*Math.PI/cols; // split values depending on index double[][] even = new double[2][cols/2]; double[][] odd = new double[2][cols/2]; - for(int j = 0; j < cols/2; j++){ - for(int i = 0; i < 2; i++){ - even[i][j] = values[i][j*2]; - odd[i][j] = values[i][j*2+1]; + + for(int i = 0; i < 2; i++){ + for (int j = 0; j < cols/2; j++){ + even[i][j] = in[i][j*2]; + odd[i][j] = in[i][j*2+1]; } } + double[][] res_even = fft_one_dim(even); + double[][] res_odd = fft_one_dim(odd); - MatrixBlock[] res_even = fft_one_dim(new MatrixBlock(1, cols/2, even[0]), new MatrixBlock(1, cols/2, even[1])); - MatrixBlock[] res_odd = fft_one_dim(new MatrixBlock(1, cols/2, odd[0]), new MatrixBlock(1, cols/2, odd[1])); - - double[][] res_even_values = new double[][]{ - res_even[0].getDenseBlockValues(), - res_even[1].getDenseBlockValues()}; - - double[][] res_odd_values = new double[][]{ - res_odd[0].getDenseBlockValues(), - res_odd[1].getDenseBlockValues()}; - - double angle = -2*Math.PI/cols; double[][] res = new double[2][cols]; - for(int j = 0; j < cols/2; j++){ - + for(int j=0; j < cols/2; j++){ double[] omega_pow = new double[]{Math.cos(j*angle), Math.sin(j*angle)}; // m = omega * res_odd[j] double[] m = new double[]{ - omega_pow[0] * res_odd_values[0][j] - omega_pow[1] * res_odd_values[1][j], - omega_pow[0] * res_odd_values[1][j] + omega_pow[1] * res_odd_values[0][j]}; + omega_pow[0] * res_odd[0][j] - omega_pow[1] * res_odd[1][j], + omega_pow[0] * res_odd[1][j] + omega_pow[1] * res_odd[0][j]}; // res[j] = res_even + m; // res[j+cols/2] = res_even - m; for(int i = 0; i < 2; i++){ - res[i][j] = res_even_values[i][j] + m[i]; - res[i][j+cols/2] = res_even_values[i][j] - m[i]; + res[i][j] = res_even[i][j] + m[i]; + res[i][j+cols/2] = res_even[i][j] - m[i]; } } - return new MatrixBlock[]{new MatrixBlock(rows, cols, res[0]), new MatrixBlock(rows, cols, res[1])}; - } + return res; - private static boolean isPowerOfTwo(int n){ - return ((n != 0) && ((n & (n - 1)) == 0)) || n == 1; } - /** - * Function to perform Fast Fourier Transformation on a given array. - * Its length has to be a power of two. - * - * @param in array of ComplexDoubles - * @return array of ComplexDoubles - */ - public static ComplexDouble[] fft(ComplexDouble[] in){ - - // TODO: how to invert fillToPowerOfTwo after calculation - // in = fillToPowerOfTwo(in); + public static double[][] ifft_one_dim(double[][] in) { - int n = in.length; - if(n == 1){ - return in; - } + // cols[0] is real part, cols[1] is imaginary part + int cols = in[0].length; - double angle = -2*Math.PI/n; + // conjugate input + in[1] = Arrays.stream(in[1]).map(i -> -i).toArray(); - ComplexDouble[] even = new ComplexDouble[n/2]; - ComplexDouble[] odd = new ComplexDouble[n/2]; - for(int i=0; i < n/2; i++){ - even[i] = in[i*2]; - odd[i] = in[i*2+1]; - } - ComplexDouble[] resEven = fft(even); - ComplexDouble[] resOdd = fft(odd); + // apply fft + double[][] res = fft_one_dim(in); - ComplexDouble[] res = new ComplexDouble[n]; - for(int j=0; j < n/2; j++){ - ComplexDouble omegaPow = new ComplexDouble(Math.cos(j*angle), Math.sin(j*angle)); + // conjugate and scale result + res[0] = Arrays.stream(res[0]).map(i -> i/cols).toArray(); + res[1] = Arrays.stream(res[1]).map(i -> -i/cols).toArray(); - res[j] = resEven[j].add(omegaPow.mul(resOdd[j])); - res[j+n/2] = resEven[j].sub(omegaPow.mul(resOdd[j])); - } - return res; } - /** - * Function to perform Fast Fourier Transformation on a given array. - * Its length has to be a power of two. - * - * @param in array of doubles - * @return array of ComplexDoubles - */ - public static ComplexDouble[] fft_old(double[] in){ - ComplexDouble[] complex = new ComplexDouble[in.length]; - for(int i=0; i -i).toArray()); - - MatrixBlock[] res = fft_one_dim(re, conj); + double[] flattened_re = Arrays.stream(in[0]).flatMapToDouble(Arrays::stream).toArray(); + double[] flattened_im = new double[rows*cols]; + if(in.length > 1){ + flattened_im = Arrays.stream(in[1]).flatMapToDouble(Arrays::stream).toArray(); + } - double[] res_re = Arrays.stream(res[0].getDenseBlockValues()).map(i -> i/cols).toArray(); - double[] res_im = Arrays.stream(res[0].getDenseBlockValues()).map(i -> -i/cols).toArray(); + MatrixBlock re = new MatrixBlock(rows, cols, flattened_re); + MatrixBlock im = new MatrixBlock(rows, cols, flattened_im); - return new MatrixBlock[]{ new MatrixBlock(rows, cols, res_re), new MatrixBlock(rows, cols, res_im)}; + return new MatrixBlock[]{re, im}; } - public static ComplexDouble[] ifft(ComplexDouble[] in) { - int n = in.length; - - // Conjugate the input array - ComplexDouble[] conjugatedInput = new ComplexDouble[n]; - for (int i = 0; i < n; i++) { - conjugatedInput[i] = in[i].conjugate(); - } - - // Apply FFT to conjugated input - ComplexDouble[] fftResult = fft(conjugatedInput); - - // Conjugate the result of FFT and scale by n - ComplexDouble[] ifftResult = new ComplexDouble[n]; - for (int i = 0; i < n; i++) { - ifftResult[i] = new ComplexDouble(fftResult[i].re / n, -fftResult[i].im / n); - } - - return ifftResult; - } - - /** - * IFFT for real-valued input. - * @param in array of doubles - * @return array of ComplexDoubles representing the IFFT - */ - public static ComplexDouble[] ifft(double[] in) { - ComplexDouble[] complexIn = new ComplexDouble[in.length]; - for (int i = 0; i < in.length; i++) { - complexIn[i] = new ComplexDouble(in[i], 0); - } - return ifft(complexIn); - } + private static MatrixBlock getZeroMatrixBlock(int rows, int cols){ - /** - * Function to fill a given array of ComplexDoubles with 0-ComplexDoubles, so that the length is a power of two. - * Needed for FastFourierTransformation - * - * @param in array of ComplexDoubles - * @return array of ComplexDoubles - */ - private static ComplexDouble[] fillToPowerOfTwo(ComplexDouble[] in){ - int missing = nextPowerOfTwo(in.length)- in.length; - ComplexDouble[] res = new ComplexDouble[in.length+missing]; - for(int i=0; i 1000){ + totalTime += (endTime - startTime); + numCalculations++; + + + + if (numCalculations % 5000 == 0) { + double averageTime = (totalTime / 1e6) / numCalculations; // Average time in milliseconds + System.out.println("\nAverage execution time after " + numCalculations + " calculations: " + String.format("%.8f", averageTime/1000) + " s \n"); + } + } + } + + reader.close(); + System.out.println("Finished processing " + lineNumber + " lines."); + } + + + + // prior to executing this test it is necessary to run the Numpy Script in FourierTestData.py and add the generated file to the root of the project. + @Test + public void testIfftWithRealNumpyData() throws IOException { + String filename = "ifft_data.csv"; // path to your IFFT data file + BufferedReader reader = new BufferedReader(new FileReader(filename)); + String line; + int lineNumber = 0; + long totalTime = 0; // Total time for all IFFT computations + int numCalculations = 0; // Number of IFFT computations + + while ((line = reader.readLine()) != null) { + lineNumber++; + String[] values = line.split(","); + int n = values.length / 3; + ComplexDouble[] originalInput = new ComplexDouble[n]; + ComplexDouble[] numpyIfftResult = new ComplexDouble[n]; + ComplexDouble[] javaIfftResult; + + for (int i = 0; i < n; i++) { + originalInput[i] = new ComplexDouble(Double.parseDouble(values[i]), 0); // Original data + double realPart = Double.parseDouble(values[n + i]); + double imagPart = Double.parseDouble(values[n * 2 + i]); + numpyIfftResult[i] = new ComplexDouble(realPart, imagPart); // NumPy IFFT result + } + + long startTime = System.nanoTime(); + javaIfftResult = ifft(originalInput); + long endTime = System.nanoTime(); + if(lineNumber > 1000){ + totalTime += (endTime - startTime); + numCalculations++; + } + for (int i = 0; i < n; i++) { + assertComplexEquals("Mismatch at index " + i + " in line " + lineNumber, numpyIfftResult[i], javaIfftResult[i]); + } + + if (numCalculations % 5000 == 0) { + double averageTime = (totalTime / 1e6) / numCalculations; // Average time in milliseconds + System.out.println("Average execution time after " + numCalculations + " calculations: " + String.format("%.8f", averageTime/1000) + " s"); + // System.out.println("input: "); + // for(int i = 0; i < originalInput.length; i++ ){ + // System.out.println(originalInput[i].toString()); + // } + // System.out.println("output: " ); + // for(int i = 0; i < javaIfftResult.length; i++ ){ + // System.out.println(javaIfftResult[i].toString()); + // } + } + } + + reader.close(); + System.out.println("Finished processing " + lineNumber + " lines."); + } + + @Test + public void testIfftWithComplexNumpyData() throws IOException { + String filename = "complex_ifft_data.csv"; // Adjusted path to your IFFT data file with complex inputs + BufferedReader reader = new BufferedReader(new FileReader(filename)); + String line; + int lineNumber = 0; + long totalTime = 0; // Total time for all IFFT computations + int numCalculations = 0; // Number of IFFT computations + + while ((line = reader.readLine()) != null) { + lineNumber++; + String[] values = line.split(","); + int n = values.length / 4; // Adjusted for complex numbers + ComplexDouble[] originalInput = new ComplexDouble[n]; + ComplexDouble[] numpyIfftResult = new ComplexDouble[n]; + ComplexDouble[] javaIfftResult; + + for (int i = 0; i < n; i++) { + double realPartOriginal = Double.parseDouble(values[i]); + double imagPartOriginal = Double.parseDouble(values[i + n]); + originalInput[i] = new ComplexDouble(realPartOriginal, imagPartOriginal); // Original complex data + + double realPartIfft = Double.parseDouble(values[i + 2 * n]); + double imagPartIfft = Double.parseDouble(values[i + 3 * n]); + numpyIfftResult[i] = new ComplexDouble(realPartIfft, imagPartIfft); // NumPy IFFT result + } + + long startTime = System.nanoTime(); + javaIfftResult = ifft(originalInput); + long endTime = System.nanoTime(); + + if(lineNumber > 1000){ + totalTime += (endTime - startTime); + numCalculations++; + } + for (int i = 0; i < n; i++) { + assertComplexEquals("Mismatch at index " + i + " in line " + lineNumber, numpyIfftResult[i], javaIfftResult[i]); + } + + if (numCalculations % 5000 == 0) { + double averageTime = (totalTime / 1e6) / numCalculations; // Average time in milliseconds + System.out.println("Average execution time after " + numCalculations + " calculations: " + String.format("%.8f", averageTime/1000) + " s"); + // System.out.println("input: "); + // for(int i = 0; i < originalInput.length; i++ ){ + // System.out.println(originalInput[i].toString()); + // } + // System.out.println("output: " ); + // for(int i = 0; i < javaIfftResult.length; i++ ){ + // System.out.println(javaIfftResult[i].toString()); + // } + + } + } + + reader.close(); + System.out.println("Finished processing " + lineNumber + " lines."); + } + + + + // prior to executing this test it is necessary to run the Numpy Script in FourierTestData.py and add the generated file to the root of the project. + @Test + public void testIfftExecutionTime() throws IOException { + String filename = "ifft_data.csv"; // path to your IFFT data file + BufferedReader reader = new BufferedReader(new FileReader(filename)); + String line; + int lineNumber = 0; + long totalTime = 0; // Total time for all IFFT computations + int numCalculations = 0; // Number of IFFT computations + + while ((line = reader.readLine()) != null) { + lineNumber++; + String[] values = line.split(","); + int n = values.length / 3; + ComplexDouble[] original = new ComplexDouble[n]; + ComplexDouble[] numpyResult = new ComplexDouble[n]; + + for (int i = 0; i < n; i++) { + original[i] = new ComplexDouble(Double.parseDouble(values[i]), 0); + double realPart = Double.parseDouble(values[n + i]); + double imagPart = Double.parseDouble(values[n * 2 + i]); + numpyResult[i] = new ComplexDouble(realPart, imagPart); + } + + long startTime = System.nanoTime(); + ifft(numpyResult); + long endTime = System.nanoTime(); + if(lineNumber > 5000){ + totalTime += (endTime - startTime); + numCalculations++; + + + if (numCalculations % 1000 == 0) { + double averageTime = (totalTime / 1e6) / numCalculations; // Average time in milliseconds + System.out.println("Average execution time after " + numCalculations + " calculations: " + String.format("%.8f", averageTime/1000) + " s"); + } + } + } + + reader.close(); + System.out.println("Finished processing " + lineNumber + " lines."); + } + + + @Test + public void testSimpleIfft2d() { + double[][] original = {{1, -2}, {3, -4}}; + ComplexDouble[][] complexInput = new ComplexDouble[original.length][original[0].length]; + for (int i = 0; i < original.length; i++) { + for (int j = 0; j < original[0].length; j++) { + complexInput[i][j] = new ComplexDouble(original[i][j], 0); + } + } + + ComplexDouble[][] fftResult = fft2d(complexInput); + ComplexDouble[][] ifftResult = ifft2d(fftResult); + + for (int i = 0; i < original.length; i++) { + for (int j = 0; j < original[0].length; j++) { + assertEquals("Mismatch at [" + i + "][" + j + "]", original[i][j], ifftResult[i][j].re, 0.000001); + assertEquals("Non-zero imaginary part at [" + i + "][" + j + "]", 0, ifftResult[i][j].im, 0.000001); + } + } + } + + + @Test + public void testFft2dWithNumpyData() throws IOException { + String filename = "complex_fft_2d_data.csv"; // path to your 2D FFT data file + BufferedReader reader = new BufferedReader(new FileReader(filename)); + String line; + int lineNumber = 0; + long totalTime = 0; // Total time for all FFT 2D computations + int numCalculations = 0; // Number of FFT 2D computations + int progressInterval = 1000; // Print progress every 1000 lines + + while ((line = reader.readLine()) != null) { + lineNumber++; + String[] values = line.split(","); + int halfLength = values.length / 4; + int sideLength = (int) Math.sqrt(halfLength); // Assuming square matrix + + ComplexDouble[][] originalInput = new ComplexDouble[sideLength][sideLength]; + ComplexDouble[][] numpyFftResult = new ComplexDouble[sideLength][sideLength]; + + for (int i = 0; i < halfLength; i++) { + int row = i / sideLength; + int col = i % sideLength; + double realPartOriginal = Double.parseDouble(values[i]); + double imagPartOriginal = Double.parseDouble(values[i + halfLength]); + originalInput[row][col] = new ComplexDouble(realPartOriginal, imagPartOriginal); + + double realPartFft = Double.parseDouble(values[i + 2 * halfLength]); + double imagPartFft = Double.parseDouble(values[i + 3 * halfLength]); + numpyFftResult[row][col] = new ComplexDouble(realPartFft, imagPartFft); + } + + long startTime = System.nanoTime(); + ComplexDouble[][] javaFftResult = fft2d(originalInput); + long endTime = System.nanoTime(); + totalTime += (endTime - startTime); + numCalculations++; + + for (int i = 0; i < sideLength; i++) { + for (int j = 0; j < sideLength; j++) { + assertComplexEquals("Mismatch at [" + i + "][" + j + "] in line " + lineNumber, numpyFftResult[i][j], javaFftResult[i][j]); + } + } + + if (lineNumber % progressInterval == 0) { // Print progress + System.out.println("Processing line: " + lineNumber); + if (numCalculations > 0) { + double averageTime = (totalTime / 1e6) / numCalculations; // Average time in milliseconds + System.out.println("Average execution time after " + numCalculations + " calculations: " + String.format("%.8f", averageTime/1000) + " s"); + } + } + } + + reader.close(); + System.out.println("Finished processing " + lineNumber + " lines. Average execution time: " + String.format("%.8f", (totalTime / 1e6 / numCalculations)/1000) + " s"); + } + + @Test + public void testIfft2dWithNumpyData() throws IOException { + String filename = "complex_ifft_2d_data.csv"; // path to your 2D IFFT data file + BufferedReader reader = new BufferedReader(new FileReader(filename)); + String line; + int lineNumber = 0; + long totalTime = 0; // Total time for all IFFT 2D computations + int numCalculations = 0; // Number of IFFT 2D computations + int progressInterval = 10000; // Print progress every 1000 lines + + while ((line = reader.readLine()) != null) { + lineNumber++; + String[] values = line.split(","); + int halfLength = values.length / 4; + int sideLength = (int) Math.sqrt(halfLength); // Assuming square matrix + + ComplexDouble[][] originalInput = new ComplexDouble[sideLength][sideLength]; + ComplexDouble[][] numpyIfftResult = new ComplexDouble[sideLength][sideLength]; + + for (int i = 0; i < halfLength; i++) { + int row = i / sideLength; + int col = i % sideLength; + double realPartOriginal = Double.parseDouble(values[i]); + double imagPartOriginal = Double.parseDouble(values[i + halfLength]); + originalInput[row][col] = new ComplexDouble(realPartOriginal, imagPartOriginal); + + double realPartIfft = Double.parseDouble(values[i + 2 * halfLength]); + double imagPartIfft = Double.parseDouble(values[i + 3 * halfLength]); + numpyIfftResult[row][col] = new ComplexDouble(realPartIfft, imagPartIfft); + } + + long startTime = System.nanoTime(); + ComplexDouble[][] javaIfftResult = ifft2d(originalInput); + long endTime = System.nanoTime(); + if(lineNumber > 1000){ + totalTime += (endTime - startTime); + numCalculations++; + } + + for (int i = 0; i < sideLength; i++) { + for (int j = 0; j < sideLength; j++) { + assertComplexEquals("Mismatch at [" + i + "][" + j + "] in line " + lineNumber, numpyIfftResult[i][j], javaIfftResult[i][j]); + } + } + + if (lineNumber % progressInterval == 0) { // Print progress + System.out.println("Processing line: " + lineNumber); + if (numCalculations > 0) { + double averageTime = (totalTime / 1e6) / numCalculations; // Average time in milliseconds + System.out.println("Average execution time after " + numCalculations + " calculations: " + String.format("%.8f", averageTime/1000) + " s"); + } + } + } + + reader.close(); + System.out.println("Finished processing " + lineNumber + " lines. Average execution time: " + String.format("%.8f", (totalTime / 1e6 / numCalculations)/1000) + " ms"); + } + + // Helper method for asserting equality with a tolerance + private static void assertEquals(String message, double expected, double actual, double tolerance) { + assertTrue(message + " - Expected: " + expected + ", Actual: " + actual, Math.abs(expected - actual) <= tolerance); + } + + private void assertComplexEquals(String message, ComplexDouble expected, ComplexDouble actual) { + final double EPSILON = 0.000001; + boolean realMatch = Math.abs(Math.abs(expected.re) - Math.abs(actual.re)) < EPSILON; + boolean imagMatch = Math.abs(Math.abs(expected.im) - Math.abs(actual.im)) < EPSILON; + + if (realMatch && imagMatch) { + if (Math.signum(expected.re) != Math.signum(actual.re)) { + System.out.println(message + " - Real part is of opposite sign but otherwise correct"); + } + if (Math.signum(expected.im) != Math.signum(actual.im)) { + System.out.println(message + " - Imaginary part is of opposite sign but otherwise correct"); + } + } else { + assertTrue(message + " - Incorrect values", false); + } + } + + @Test + public void simpleTest() { + // tested with numpy + double[] in = {0, 18, -15, 3}; + ComplexDouble[] expected = new ComplexDouble[4]; + expected[0] = new ComplexDouble(6, 0); + expected[1] = new ComplexDouble(15, -15); + expected[2] = new ComplexDouble(-36, 0); + expected[3] = new ComplexDouble(15, 15); + + ComplexDouble[] res = fft_old(in); + for(ComplexDouble elem : res){ + System.out.println(elem); + } + + assertArrayEquals(expected, res); + } + + @Test + public void notPowerOfTwoTest() { + double[] in = {1, 2, 3, 4, 5}; + + // see https://de.mathworks.com/help/matlab/ref/ifft.html + ComplexDouble[] expected = new ComplexDouble[5]; + expected[0] = new ComplexDouble(15,0); + expected[1] = new ComplexDouble(-2.5000,3.4410); + expected[2] = new ComplexDouble(-2.5000,0.8123); + expected[3] = new ComplexDouble(-2.5000, - 0.8123); + expected[4] = new ComplexDouble(-2.5000, - 3.4410); + + ComplexDouble[] res = fft_old(in); + for(ComplexDouble elem : res){ + System.out.println(elem); + } + assertArrayEquals(expected, res); + } + + @Test + public void simple2dTest() { + // tested with matlab + double[][] in = {{0, 18}, {-15, 3}}; + ComplexDouble[][] expected = new ComplexDouble[2][2]; + expected[0][0] = new ComplexDouble(6, 0); + expected[0][1] = new ComplexDouble(-36, 0); + expected[1][0] = new ComplexDouble(30, 0); + expected[1][1] = new ComplexDouble(0, 0); + + ComplexDouble[][] res = fft2d(in); + for(ComplexDouble[] row : res){ + for(ComplexDouble elem : row){ + System.out.println(elem); + } + } + + assertArrayEquals(expected, res); + } + + @Test + public void simple2dTest2() { + double[][] in = {{0, 18, -15, 3}, {0, 18, -15, 3}}; + ComplexDouble[][] expected = new ComplexDouble[2][4]; + expected[0][0] = new ComplexDouble(12, 0); + expected[0][1] = new ComplexDouble(30, -30); + expected[0][2] = new ComplexDouble(-72, 0); + expected[0][3] = new ComplexDouble(30, 30); + expected[1][0] = new ComplexDouble(0, 0); + expected[1][1] = new ComplexDouble(0, 0); + expected[1][2] = new ComplexDouble(0, 0); + expected[1][3] = new ComplexDouble(0, 0); + + ComplexDouble[][] res = fft2d(in); + for(ComplexDouble[] row : res){ + for(ComplexDouble elem : row){ + System.out.println(elem); + } + } + + assertArrayEquals(expected, res); + } + + @Test + public void simple2dTest2SecondPart() { + // ComplexDouble(15, -15) is the second (expected[1]) entry of simpleTest + // this tests the col computation in fft2d for simple2dTest2 + + ComplexDouble[] in = new ComplexDouble[2]; + in[0] = new ComplexDouble(15, -15); + in[1] = new ComplexDouble(15, -15); + + ComplexDouble[] expected = new ComplexDouble[2]; + expected[0] = new ComplexDouble(30, -30); + expected[1] = new ComplexDouble(0, 0); + + ComplexDouble[] res = fft(in); + for (ComplexDouble elem : res) { + System.out.println(elem); + } + + assertArrayEquals(expected, res); + } + + + + @Test + public void testSimpleIfft1d() { + double[] original = {1, -2, 3, -4}; + ComplexDouble[] complexInput = new ComplexDouble[original.length]; + for (int i = 0; i < original.length; i++) { + complexInput[i] = new ComplexDouble(original[i], 0); + } + + ComplexDouble[] fftResult = fft(complexInput); + ComplexDouble[] ifftResult = ifft(fftResult); + + for (int i = 0; i < original.length; i++) { + assertEquals("Mismatch at index " + i, original[i], ifftResult[i].re, 0.000001); + assertEquals("Non-zero imaginary part at index " + i, 0, ifftResult[i].im, 0.000001); + } + } + +} diff --git a/src/test/java/org/apache/sysds/test/component/matrix/FourierTest.java b/src/test/java/org/apache/sysds/test/component/matrix/FourierTest.java index 0a842445011..0fdbd681676 100644 --- a/src/test/java/org/apache/sysds/test/component/matrix/FourierTest.java +++ b/src/test/java/org/apache/sysds/test/component/matrix/FourierTest.java @@ -14,154 +14,66 @@ import java.io.IOException; public class FourierTest { - - - // prior to executing this test it is necessary to run the Numpy Script in FourierTestData.py and add the generated file to the root of the project. - @Test - public void testFftWithNumpyData() throws IOException { - String filename = "fft_data.csv"; // path to your CSV file - BufferedReader reader = new BufferedReader(new FileReader(filename)); - String line; - int lineNumber = 0; - - while ((line = reader.readLine()) != null) { - lineNumber++; - if (lineNumber % 1000 == 0) { // Print progress every 1000 lines - System.out.println("Processing line: " + lineNumber); - } - - String[] values = line.split(","); - int n = values.length / 3; - double[] input = new double[n]; - ComplexDouble[] expected = new ComplexDouble[n]; - ComplexDouble[] actual; - - for (int i = 0; i < n; i++) { - input[i] = Double.parseDouble(values[i]); - double real = Double.parseDouble(values[n + i]); - double imag = Double.parseDouble(values[n * 2 + i]); - expected[i] = new ComplexDouble(real, imag); - } - - actual = fft_old(input); - - for (int i = 0; i < n; i++) { - assertComplexEquals("Mismatch at index " + i + " in line " + lineNumber, expected[i], actual[i]); - } - } - - reader.close(); - System.out.println("Finished processing " + lineNumber + " lines."); - } - - // prior to executing this test it is necessary to run the Numpy Script in FourierTestData.py and add the generated file to the root of the project. @Test - public void testFftExecutionTime() throws IOException { - String filename = "fft_data.csv"; // path to your CSV file - BufferedReader reader = new BufferedReader(new FileReader(filename)); - String line; - int lineNumber = 0; - long totalTime = 0; // Total time for all FFT computations - int numCalculations = 0; // Number of FFT computations - - while ((line = reader.readLine()) != null) { - lineNumber++; - String[] values = line.split(","); - int n = values.length / 3; - double[] input = new double[n]; - ComplexDouble[] expected = new ComplexDouble[n]; - - for (int i = 0; i < n; i++) { - input[i] = Double.parseDouble(values[i]); - double real = Double.parseDouble(values[n + i]); - double imag = Double.parseDouble(values[n * 2 + i]); - expected[i] = new ComplexDouble(real, imag); - } - - long startTime = System.nanoTime(); - fft_old(input); - long endTime = System.nanoTime(); - if(lineNumber > 1000){ - totalTime += (endTime - startTime); - numCalculations++; - - - - if (numCalculations % 5000 == 0) { - double averageTime = (totalTime / 1e6) / numCalculations; // Average time in milliseconds - System.out.println("\nAverage execution time after " + numCalculations + " calculations: " + String.format("%.8f", averageTime/1000) + " s \n"); - } - } - } - - reader.close(); - System.out.println("Finished processing " + lineNumber + " lines."); - } + public void simple_test_one_dim() { + // 1st row real part, 2nd row imaginary part + double[][] in = {{0, 18, -15, 3},{0, 0, 0, 0}}; + double[][] expected = {{6, 15, -36, 15},{0, -15, 0, 15}}; - private void assertComplexEquals(String message, ComplexDouble expected, ComplexDouble actual) { - final double EPSILON = 0.000001; - boolean realMatch = Math.abs(Math.abs(expected.re) - Math.abs(actual.re)) < EPSILON; - boolean imagMatch = Math.abs(Math.abs(expected.im) - Math.abs(actual.im)) < EPSILON; - - if (realMatch && imagMatch) { - if (Math.signum(expected.re) != Math.signum(actual.re)) { - System.out.println(message + " - Real part is of opposite sign but otherwise correct"); - } - if (Math.signum(expected.im) != Math.signum(actual.im)) { - System.out.println(message + " - Imaginary part is of opposite sign but otherwise correct"); + double[][] res = fft_one_dim(in); + for(double[] row : res){ + for (double elem : row){ + System.out.print(elem + " "); } - } else { - assertTrue(message + " - Incorrect values", false); + System.out.println(); } + assertArrayEquals(expected[0], res[0], 0.0001); + assertArrayEquals(expected[1], res[1], 0.0001); } - + @Test - public void simpleTest() { + public void simple_test_two_dim() { // tested with numpy - double[] in = {0, 18, -15, 3}; - ComplexDouble[] expected = new ComplexDouble[4]; - expected[0] = new ComplexDouble(6, 0); - expected[1] = new ComplexDouble(15, -15); - expected[2] = new ComplexDouble(-36, 0); - expected[3] = new ComplexDouble(15, 15); + double[][][] in = {{{0, 18},{-15, 3}},{{0, 0},{0, 0}}}; + + double[][][] expected = {{{6, -36},{30, 0}},{{0, 0},{0, 0}}}; - ComplexDouble[] res = fft_old(in); - for(ComplexDouble elem : res){ - System.out.println(elem); + double[][][] res = fft(in, false); + + for(double[][] matrix : res){ + for(double[] row : matrix) { + for (double elem : row) { + System.out.print(elem + " "); + } + System.out.println(); + } + System.out.println(); } - assertArrayEquals(expected, res); + for(int k = 0; k < 2 ; k++){ + for(int i = 0; i < res[0].length; i++) { + assertArrayEquals(expected[k][i], res[k][i], 0.0001); + } + } } @Test - public void matrix_block_one_dim_test(){ - - double[] in = {0, 18, -15, 3}; - - double[] expected_re = {6,15,-36,15}; - double[] expected_im = {0,-15,0,15}; + public void simple_test_one_dim_ifft() { - MatrixBlock[] res = fft(in); - double[] res_re = res[0].getDenseBlockValues(); - double[] res_im = res[1].getDenseBlockValues(); + double[][] in = {{1, -2, 3, -4},{0, 0, 0, 0}}; - for(double elem : res_re){ - System.out.print(elem+" "); - } - System.out.println(); - for(double elem : res_im){ - System.out.print(elem+" "); - } + double[][] res_fft = fft_one_dim(in); + double[][] res = ifft_one_dim(res_fft); - assertArrayEquals(expected_re, res_re, 0.0001); - assertArrayEquals(expected_im, res_im, 0.0001); + assertArrayEquals(in[0], res[0], 0.0001); + assertArrayEquals(in[1], res[1], 0.0001); } @Test - public void matrix_block_one_dim_as_two_dim_test(){ + public void matrix_block_one_dim_test(){ - double[][] in = {{0, 18, -15, 3}}; + double[] in = {0, 18, -15, 3}; double[] expected_re = {6,15,-36,15}; double[] expected_im = {0,-15,0,15}; @@ -181,11 +93,10 @@ public void matrix_block_one_dim_as_two_dim_test(){ assertArrayEquals(expected_re, res_re, 0.0001); assertArrayEquals(expected_im, res_im, 0.0001); } - @Test public void matrix_block_two_dim_test(){ - double[][] in = {{0, 18}, {-15, 3}}; + double[][][] in = {{{0, 18},{-15, 3}}}; double[] flattened_expected_re = {6,-36, 30,0}; double[] flattened_expected_im = {0,0,0,0}; @@ -206,403 +117,4 @@ public void matrix_block_two_dim_test(){ assertArrayEquals(flattened_expected_im, res_im, 0.0001); } - - @Test - public void notPowerOfTwoTest() { - double[] in = {1, 2, 3, 4, 5}; - - // see https://de.mathworks.com/help/matlab/ref/ifft.html - ComplexDouble[] expected = new ComplexDouble[5]; - expected[0] = new ComplexDouble(15,0); - expected[1] = new ComplexDouble(-2.5000,3.4410); - expected[2] = new ComplexDouble(-2.5000,0.8123); - expected[3] = new ComplexDouble(-2.5000, - 0.8123); - expected[4] = new ComplexDouble(-2.5000, - 3.4410); - - ComplexDouble[] res = fft_old(in); - for(ComplexDouble elem : res){ - System.out.println(elem); - } - assertArrayEquals(expected, res); - } - - @Test - public void simple2dTest() { - // tested with matlab - double[][] in = {{0, 18}, {-15, 3}}; - ComplexDouble[][] expected = new ComplexDouble[2][2]; - expected[0][0] = new ComplexDouble(6, 0); - expected[0][1] = new ComplexDouble(-36, 0); - expected[1][0] = new ComplexDouble(30, 0); - expected[1][1] = new ComplexDouble(0, 0); - - ComplexDouble[][] res = fft2d(in); - for(ComplexDouble[] row : res){ - for(ComplexDouble elem : row){ - System.out.println(elem); - } - } - - assertArrayEquals(expected, res); - } - - @Test - public void simple2dTest2() { - double[][] in = {{0, 18, -15, 3}, {0, 18, -15, 3}}; - ComplexDouble[][] expected = new ComplexDouble[2][4]; - expected[0][0] = new ComplexDouble(12, 0); - expected[0][1] = new ComplexDouble(30, -30); - expected[0][2] = new ComplexDouble(-72, 0); - expected[0][3] = new ComplexDouble(30, 30); - expected[1][0] = new ComplexDouble(0, 0); - expected[1][1] = new ComplexDouble(0, 0); - expected[1][2] = new ComplexDouble(0, 0); - expected[1][3] = new ComplexDouble(0, 0); - - ComplexDouble[][] res = fft2d(in); - for(ComplexDouble[] row : res){ - for(ComplexDouble elem : row){ - System.out.println(elem); - } - } - - assertArrayEquals(expected, res); - } - - @Test - public void simple2dTest2SecondPart() { - // ComplexDouble(15, -15) is the second (expected[1]) entry of simpleTest - // this tests the col computation in fft2d for simple2dTest2 - - ComplexDouble[] in = new ComplexDouble[2]; - in[0] = new ComplexDouble(15, -15); - in[1] = new ComplexDouble(15, -15); - - ComplexDouble[] expected = new ComplexDouble[2]; - expected[0] = new ComplexDouble(30, -30); - expected[1] = new ComplexDouble(0, 0); - - ComplexDouble[] res = fft(in); - for (ComplexDouble elem : res) { - System.out.println(elem); - } - - assertArrayEquals(expected, res); - } - - @Test - public void testSimpleIfft1d() { - double[] original = {1, -2, 3, -4}; - ComplexDouble[] complexInput = new ComplexDouble[original.length]; - for (int i = 0; i < original.length; i++) { - complexInput[i] = new ComplexDouble(original[i], 0); - } - - ComplexDouble[] fftResult = fft(complexInput); - ComplexDouble[] ifftResult = ifft(fftResult); - - for (int i = 0; i < original.length; i++) { - assertEquals("Mismatch at index " + i, original[i], ifftResult[i].re, 0.000001); - assertEquals("Non-zero imaginary part at index " + i, 0, ifftResult[i].im, 0.000001); - } - } - - // prior to executing this test it is necessary to run the Numpy Script in FourierTestData.py and add the generated file to the root of the project. - @Test - public void testIfftWithRealNumpyData() throws IOException { - String filename = "ifft_data.csv"; // path to your IFFT data file - BufferedReader reader = new BufferedReader(new FileReader(filename)); - String line; - int lineNumber = 0; - long totalTime = 0; // Total time for all IFFT computations - int numCalculations = 0; // Number of IFFT computations - - while ((line = reader.readLine()) != null) { - lineNumber++; - String[] values = line.split(","); - int n = values.length / 3; - ComplexDouble[] originalInput = new ComplexDouble[n]; - ComplexDouble[] numpyIfftResult = new ComplexDouble[n]; - ComplexDouble[] javaIfftResult; - - for (int i = 0; i < n; i++) { - originalInput[i] = new ComplexDouble(Double.parseDouble(values[i]), 0); // Original data - double realPart = Double.parseDouble(values[n + i]); - double imagPart = Double.parseDouble(values[n * 2 + i]); - numpyIfftResult[i] = new ComplexDouble(realPart, imagPart); // NumPy IFFT result - } - - long startTime = System.nanoTime(); - javaIfftResult = ifft(originalInput); - long endTime = System.nanoTime(); - if(lineNumber > 1000){ - totalTime += (endTime - startTime); - numCalculations++; - } - for (int i = 0; i < n; i++) { - assertComplexEquals("Mismatch at index " + i + " in line " + lineNumber, numpyIfftResult[i], javaIfftResult[i]); - } - - if (numCalculations % 5000 == 0) { - double averageTime = (totalTime / 1e6) / numCalculations; // Average time in milliseconds - System.out.println("Average execution time after " + numCalculations + " calculations: " + String.format("%.8f", averageTime/1000) + " s"); - // System.out.println("input: "); - // for(int i = 0; i < originalInput.length; i++ ){ - // System.out.println(originalInput[i].toString()); - // } - // System.out.println("output: " ); - // for(int i = 0; i < javaIfftResult.length; i++ ){ - // System.out.println(javaIfftResult[i].toString()); - // } - } - } - - reader.close(); - System.out.println("Finished processing " + lineNumber + " lines."); - } - - @Test - public void testIfftWithComplexNumpyData() throws IOException { - String filename = "complex_ifft_data.csv"; // Adjusted path to your IFFT data file with complex inputs - BufferedReader reader = new BufferedReader(new FileReader(filename)); - String line; - int lineNumber = 0; - long totalTime = 0; // Total time for all IFFT computations - int numCalculations = 0; // Number of IFFT computations - - while ((line = reader.readLine()) != null) { - lineNumber++; - String[] values = line.split(","); - int n = values.length / 4; // Adjusted for complex numbers - ComplexDouble[] originalInput = new ComplexDouble[n]; - ComplexDouble[] numpyIfftResult = new ComplexDouble[n]; - ComplexDouble[] javaIfftResult; - - for (int i = 0; i < n; i++) { - double realPartOriginal = Double.parseDouble(values[i]); - double imagPartOriginal = Double.parseDouble(values[i + n]); - originalInput[i] = new ComplexDouble(realPartOriginal, imagPartOriginal); // Original complex data - - double realPartIfft = Double.parseDouble(values[i + 2 * n]); - double imagPartIfft = Double.parseDouble(values[i + 3 * n]); - numpyIfftResult[i] = new ComplexDouble(realPartIfft, imagPartIfft); // NumPy IFFT result - } - - long startTime = System.nanoTime(); - javaIfftResult = ifft(originalInput); - long endTime = System.nanoTime(); - - if(lineNumber > 1000){ - totalTime += (endTime - startTime); - numCalculations++; - } - for (int i = 0; i < n; i++) { - assertComplexEquals("Mismatch at index " + i + " in line " + lineNumber, numpyIfftResult[i], javaIfftResult[i]); - } - - if (numCalculations % 5000 == 0) { - double averageTime = (totalTime / 1e6) / numCalculations; // Average time in milliseconds - System.out.println("Average execution time after " + numCalculations + " calculations: " + String.format("%.8f", averageTime/1000) + " s"); - // System.out.println("input: "); - // for(int i = 0; i < originalInput.length; i++ ){ - // System.out.println(originalInput[i].toString()); - // } - // System.out.println("output: " ); - // for(int i = 0; i < javaIfftResult.length; i++ ){ - // System.out.println(javaIfftResult[i].toString()); - // } - - } - } - - reader.close(); - System.out.println("Finished processing " + lineNumber + " lines."); - } - - - // prior to executing this test it is necessary to run the Numpy Script in FourierTestData.py and add the generated file to the root of the project. - @Test - public void testIfftExecutionTime() throws IOException { - String filename = "ifft_data.csv"; // path to your IFFT data file - BufferedReader reader = new BufferedReader(new FileReader(filename)); - String line; - int lineNumber = 0; - long totalTime = 0; // Total time for all IFFT computations - int numCalculations = 0; // Number of IFFT computations - - while ((line = reader.readLine()) != null) { - lineNumber++; - String[] values = line.split(","); - int n = values.length / 3; - ComplexDouble[] original = new ComplexDouble[n]; - ComplexDouble[] numpyResult = new ComplexDouble[n]; - - for (int i = 0; i < n; i++) { - original[i] = new ComplexDouble(Double.parseDouble(values[i]), 0); - double realPart = Double.parseDouble(values[n + i]); - double imagPart = Double.parseDouble(values[n * 2 + i]); - numpyResult[i] = new ComplexDouble(realPart, imagPart); - } - - long startTime = System.nanoTime(); - ifft(numpyResult); - long endTime = System.nanoTime(); - if(lineNumber > 5000){ - totalTime += (endTime - startTime); - numCalculations++; - - - if (numCalculations % 1000 == 0) { - double averageTime = (totalTime / 1e6) / numCalculations; // Average time in milliseconds - System.out.println("Average execution time after " + numCalculations + " calculations: " + String.format("%.8f", averageTime/1000) + " s"); - } - } - } - - reader.close(); - System.out.println("Finished processing " + lineNumber + " lines."); - } - - - @Test - public void testSimpleIfft2d() { - double[][] original = {{1, -2}, {3, -4}}; - ComplexDouble[][] complexInput = new ComplexDouble[original.length][original[0].length]; - for (int i = 0; i < original.length; i++) { - for (int j = 0; j < original[0].length; j++) { - complexInput[i][j] = new ComplexDouble(original[i][j], 0); - } - } - - ComplexDouble[][] fftResult = fft2d(complexInput); - ComplexDouble[][] ifftResult = ifft2d(fftResult); - - for (int i = 0; i < original.length; i++) { - for (int j = 0; j < original[0].length; j++) { - assertEquals("Mismatch at [" + i + "][" + j + "]", original[i][j], ifftResult[i][j].re, 0.000001); - assertEquals("Non-zero imaginary part at [" + i + "][" + j + "]", 0, ifftResult[i][j].im, 0.000001); - } - } - } - - // Helper method for asserting equality with a tolerance - private static void assertEquals(String message, double expected, double actual, double tolerance) { - assertTrue(message + " - Expected: " + expected + ", Actual: " + actual, Math.abs(expected - actual) <= tolerance); - } - - @Test - public void testFft2dWithNumpyData() throws IOException { - String filename = "complex_fft_2d_data.csv"; // path to your 2D FFT data file - BufferedReader reader = new BufferedReader(new FileReader(filename)); - String line; - int lineNumber = 0; - long totalTime = 0; // Total time for all FFT 2D computations - int numCalculations = 0; // Number of FFT 2D computations - int progressInterval = 1000; // Print progress every 1000 lines - - while ((line = reader.readLine()) != null) { - lineNumber++; - String[] values = line.split(","); - int halfLength = values.length / 4; - int sideLength = (int) Math.sqrt(halfLength); // Assuming square matrix - - ComplexDouble[][] originalInput = new ComplexDouble[sideLength][sideLength]; - ComplexDouble[][] numpyFftResult = new ComplexDouble[sideLength][sideLength]; - - for (int i = 0; i < halfLength; i++) { - int row = i / sideLength; - int col = i % sideLength; - double realPartOriginal = Double.parseDouble(values[i]); - double imagPartOriginal = Double.parseDouble(values[i + halfLength]); - originalInput[row][col] = new ComplexDouble(realPartOriginal, imagPartOriginal); - - double realPartFft = Double.parseDouble(values[i + 2 * halfLength]); - double imagPartFft = Double.parseDouble(values[i + 3 * halfLength]); - numpyFftResult[row][col] = new ComplexDouble(realPartFft, imagPartFft); - } - - long startTime = System.nanoTime(); - ComplexDouble[][] javaFftResult = fft2d(originalInput); - long endTime = System.nanoTime(); - totalTime += (endTime - startTime); - numCalculations++; - - for (int i = 0; i < sideLength; i++) { - for (int j = 0; j < sideLength; j++) { - assertComplexEquals("Mismatch at [" + i + "][" + j + "] in line " + lineNumber, numpyFftResult[i][j], javaFftResult[i][j]); - } - } - - if (lineNumber % progressInterval == 0) { // Print progress - System.out.println("Processing line: " + lineNumber); - if (numCalculations > 0) { - double averageTime = (totalTime / 1e6) / numCalculations; // Average time in milliseconds - System.out.println("Average execution time after " + numCalculations + " calculations: " + String.format("%.8f", averageTime/1000) + " s"); - } - } - } - - reader.close(); - System.out.println("Finished processing " + lineNumber + " lines. Average execution time: " + String.format("%.8f", (totalTime / 1e6 / numCalculations)/1000) + " s"); - } - - @Test - public void testIfft2dWithNumpyData() throws IOException { - String filename = "complex_ifft_2d_data.csv"; // path to your 2D IFFT data file - BufferedReader reader = new BufferedReader(new FileReader(filename)); - String line; - int lineNumber = 0; - long totalTime = 0; // Total time for all IFFT 2D computations - int numCalculations = 0; // Number of IFFT 2D computations - int progressInterval = 10000; // Print progress every 1000 lines - - while ((line = reader.readLine()) != null) { - lineNumber++; - String[] values = line.split(","); - int halfLength = values.length / 4; - int sideLength = (int) Math.sqrt(halfLength); // Assuming square matrix - - ComplexDouble[][] originalInput = new ComplexDouble[sideLength][sideLength]; - ComplexDouble[][] numpyIfftResult = new ComplexDouble[sideLength][sideLength]; - - for (int i = 0; i < halfLength; i++) { - int row = i / sideLength; - int col = i % sideLength; - double realPartOriginal = Double.parseDouble(values[i]); - double imagPartOriginal = Double.parseDouble(values[i + halfLength]); - originalInput[row][col] = new ComplexDouble(realPartOriginal, imagPartOriginal); - - double realPartIfft = Double.parseDouble(values[i + 2 * halfLength]); - double imagPartIfft = Double.parseDouble(values[i + 3 * halfLength]); - numpyIfftResult[row][col] = new ComplexDouble(realPartIfft, imagPartIfft); - } - - long startTime = System.nanoTime(); - ComplexDouble[][] javaIfftResult = ifft2d(originalInput); - long endTime = System.nanoTime(); - if(lineNumber > 1000){ - totalTime += (endTime - startTime); - numCalculations++; - } - - for (int i = 0; i < sideLength; i++) { - for (int j = 0; j < sideLength; j++) { - assertComplexEquals("Mismatch at [" + i + "][" + j + "] in line " + lineNumber, numpyIfftResult[i][j], javaIfftResult[i][j]); - } - } - - if (lineNumber % progressInterval == 0) { // Print progress - System.out.println("Processing line: " + lineNumber); - if (numCalculations > 0) { - double averageTime = (totalTime / 1e6) / numCalculations; // Average time in milliseconds - System.out.println("Average execution time after " + numCalculations + " calculations: " + String.format("%.8f", averageTime/1000) + " s"); - } - } - } - - reader.close(); - System.out.println("Finished processing " + lineNumber + " lines. Average execution time: " + String.format("%.8f", (totalTime / 1e6 / numCalculations)/1000) + " ms"); - } - - } \ No newline at end of file From 011568f7896c2911473f9b3d54c8aa4205fc3b5d Mon Sep 17 00:00:00 2001 From: psej Date: Thu, 21 Dec 2023 01:06:05 +0100 Subject: [PATCH 020/133] FastMath --- .../sysds/runtime/matrix/data/LibMatrixFourier.java | 3 ++- .../apache/sysds/test/component/matrix/FourierTest.java | 8 -------- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java index 94a5523d1ba..ff36daa393c 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java @@ -1,5 +1,6 @@ package org.apache.sysds.runtime.matrix.data; +import org.apache.commons.math3.util.FastMath; import java.util.Arrays; public class LibMatrixFourier { @@ -105,7 +106,7 @@ public static double[][] fft_one_dim(double[][] in){ double[][] res = new double[2][cols]; for(int j=0; j < cols/2; j++){ - double[] omega_pow = new double[]{Math.cos(j*angle), Math.sin(j*angle)}; + double[] omega_pow = new double[]{FastMath.cos(j*angle), FastMath.sin(j*angle)}; // m = omega * res_odd[j] double[] m = new double[]{ diff --git a/src/test/java/org/apache/sysds/test/component/matrix/FourierTest.java b/src/test/java/org/apache/sysds/test/component/matrix/FourierTest.java index 0fdbd681676..3c52055b458 100644 --- a/src/test/java/org/apache/sysds/test/component/matrix/FourierTest.java +++ b/src/test/java/org/apache/sysds/test/component/matrix/FourierTest.java @@ -1,17 +1,9 @@ package org.apache.sysds.test.component.matrix; -import org.apache.sysds.runtime.matrix.data.ComplexDouble; - import org.apache.sysds.runtime.matrix.data.MatrixBlock; import org.junit.Test; - import static org.apache.sysds.runtime.matrix.data.LibMatrixFourier.*; import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertTrue; - -import java.io.BufferedReader; -import java.io.FileReader; -import java.io.IOException; public class FourierTest { From f6ae6893edc878ee501d8d5c54b5e24977d576fe Mon Sep 17 00:00:00 2001 From: psej Date: Thu, 21 Dec 2023 17:32:53 +0100 Subject: [PATCH 021/133] fft integration in systemds --- .../java/org/apache/sysds/hops/FunctionOp.java | 9 +++++++++ .../org/apache/sysds/parser/DMLTranslator.java | 1 + .../runtime/instructions/CPInstructionParser.java | 1 + .../cp/MultiReturnBuiltinCPInstruction.java | 9 +++++++++ .../sysds/runtime/matrix/data/LibCommonsMath.java | 14 +++++++++++++- .../runtime/matrix/data/LibMatrixFourier.java | 2 +- 6 files changed, 34 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/apache/sysds/hops/FunctionOp.java b/src/main/java/org/apache/sysds/hops/FunctionOp.java index 45f21e975b5..e0875b3fe1d 100644 --- a/src/main/java/org/apache/sysds/hops/FunctionOp.java +++ b/src/main/java/org/apache/sysds/hops/FunctionOp.java @@ -201,6 +201,11 @@ else if ( getFunctionName().equalsIgnoreCase("eigen") ) { long outputValues = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(1).getDim1(), 1, 1.0); return outputVectors+outputValues; } + else if ( getFunctionName().equalsIgnoreCase("fft") ) { + long outputRe = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(0).getDim1(), getOutputs().get(0).getDim2(), 1.0); + long outputIm = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(1).getDim1(), getOutputs().get(1).getDim2(), 1.0); + return outputRe+outputIm; + } else if ( getFunctionName().equalsIgnoreCase("lstm") || getFunctionName().equalsIgnoreCase("lstm_backward") ) { // TODO: To allow for initial version to always run on the GPU return 0; @@ -250,6 +255,10 @@ else if ( getFunctionName().equalsIgnoreCase("eigen")) { return OptimizerUtils.estimateSizeExactSparsity(getInput().get(0).getDim1(), getInput().get(0).getDim2(), 1.0) + 3*OptimizerUtils.estimateSizeExactSparsity(getInput().get(0).getDim1(), 1, 1.0); } + if ( getFunctionName().equalsIgnoreCase("fft") ) { + // 2 matrices of size same as the input + return 2*OptimizerUtils.estimateSizeExactSparsity(getInput().get(0).getDim1(), getInput().get(0).getDim2(), 1.0); + } else if (getFunctionName().equalsIgnoreCase("batch_norm2d") || getFunctionName().equalsIgnoreCase("batch_norm2d_backward") || getFunctionName().equalsIgnoreCase("batch_norm2d_train") || getFunctionName().equalsIgnoreCase("batch_norm2d_test")) { return 0; diff --git a/src/main/java/org/apache/sysds/parser/DMLTranslator.java b/src/main/java/org/apache/sysds/parser/DMLTranslator.java index 0ff6ca6f038..f4ff48fb730 100644 --- a/src/main/java/org/apache/sysds/parser/DMLTranslator.java +++ b/src/main/java/org/apache/sysds/parser/DMLTranslator.java @@ -2237,6 +2237,7 @@ private Hop processMultipleReturnBuiltinFunctionExpression(BuiltinFunctionExpres case QR: case LU: case EIGEN: + case FFT: case LSTM: case LSTM_BACKWARD: case BATCH_NORM2D: diff --git a/src/main/java/org/apache/sysds/runtime/instructions/CPInstructionParser.java b/src/main/java/org/apache/sysds/runtime/instructions/CPInstructionParser.java index 3de9fcd65df..28e8102f2bf 100644 --- a/src/main/java/org/apache/sysds/runtime/instructions/CPInstructionParser.java +++ b/src/main/java/org/apache/sysds/runtime/instructions/CPInstructionParser.java @@ -330,6 +330,7 @@ public class CPInstructionParser extends InstructionParser { String2CPInstructionType.put( "qr", CPType.MultiReturnBuiltin); String2CPInstructionType.put( "lu", CPType.MultiReturnBuiltin); String2CPInstructionType.put( "eigen", CPType.MultiReturnBuiltin); + String2CPInstructionType.put( "fft", CPType.MultiReturnBuiltin); String2CPInstructionType.put( "svd", CPType.MultiReturnBuiltin); String2CPInstructionType.put( "partition", CPType.Partition); diff --git a/src/main/java/org/apache/sysds/runtime/instructions/cp/MultiReturnBuiltinCPInstruction.java b/src/main/java/org/apache/sysds/runtime/instructions/cp/MultiReturnBuiltinCPInstruction.java index 0d28a62f4bb..991d910877e 100644 --- a/src/main/java/org/apache/sysds/runtime/instructions/cp/MultiReturnBuiltinCPInstruction.java +++ b/src/main/java/org/apache/sysds/runtime/instructions/cp/MultiReturnBuiltinCPInstruction.java @@ -89,6 +89,15 @@ else if ( opcode.equalsIgnoreCase("eigen") ) { return new MultiReturnBuiltinCPInstruction(null, in1, outputs, opcode, str); + } + else if ( opcode.equalsIgnoreCase("fft") ) { + // one input and two outputs + CPOperand in1 = new CPOperand(parts[1]); + outputs.add ( new CPOperand(parts[2], ValueType.FP64, DataType.MATRIX) ); + outputs.add ( new CPOperand(parts[3], ValueType.FP64, DataType.MATRIX) ); + + return new MultiReturnBuiltinCPInstruction(null, in1, outputs, opcode, str); + } else if ( opcode.equalsIgnoreCase("svd") ) { CPOperand in1 = new CPOperand(parts[1]); diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java index cc399bac791..efa9ac56f8c 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java @@ -49,6 +49,8 @@ import org.apache.sysds.runtime.matrix.operators.BinaryOperator; import org.apache.sysds.runtime.util.DataConverter; +import static org.apache.sysds.runtime.matrix.data.LibMatrixFourier.fft; + /** * Library for matrix operations that need invocation of * Apache Commons Math library. @@ -71,7 +73,7 @@ public static boolean isSupportedUnaryOperation( String opcode ) { } public static boolean isSupportedMultiReturnOperation( String opcode ) { - return ( opcode.equals("qr") || opcode.equals("lu") || opcode.equals("eigen") || opcode.equals("svd") ); + return ( opcode.equals("qr") || opcode.equals("lu") || opcode.equals("eigen") || opcode.equals("fft") || opcode.equals("svd") ); } public static boolean isSupportedMatrixMatrixOperation( String opcode ) { @@ -111,6 +113,8 @@ else if (opcode.equals("eigen_lanczos")) return computeEigenLanczos(in, threads, seed); else if (opcode.equals("eigen_qr")) return computeEigenQR(in, threads); + else if (opcode.equals("fft")) + return computeFFT(in); else if (opcode.equals("svd")) return computeSvd(in); return null; @@ -252,6 +256,14 @@ private static EigenDecomposition computeEigenRegularized(MatrixBlock in) { DataConverter.convertToArray2DRowRealMatrix(in2)); } + private static MatrixBlock[] computeFFT(MatrixBlock in) { + if( in == null || in.isEmptyBlock(false) ) + throw new DMLRuntimeException("Invalid empty block"); + + //run fft + return fft(in); + } + /** * Performs Singular Value Decomposition. Calls Apache Commons Math SVD. * X = U * Sigma * Vt, where X is the input matrix, diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java index ff36daa393c..1c9fee6642e 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java @@ -88,7 +88,7 @@ public static double[][] fft_one_dim(double[][] in){ int cols = in[0].length; if(cols == 1) return in; - double angle = -2*Math.PI/cols; + double angle = -2*FastMath.PI/cols; // split values depending on index double[][] even = new double[2][cols/2]; From e51c82536a123e2ab53510002ba9b8c75e83e9e0 Mon Sep 17 00:00:00 2001 From: Frederic Zoepffel Date: Fri, 22 Dec 2023 15:02:06 +0100 Subject: [PATCH 022/133] fixed a integration bug and adjusted tests on csv files to new functions --- .../org/apache/sysds/hops/FunctionOp.java | 2 +- .../matrix/FourierTestWithFiles.java | 378 ++++++++++++++++++ 2 files changed, 379 insertions(+), 1 deletion(-) create mode 100644 src/test/java/org/apache/sysds/test/component/matrix/FourierTestWithFiles.java diff --git a/src/main/java/org/apache/sysds/hops/FunctionOp.java b/src/main/java/org/apache/sysds/hops/FunctionOp.java index e0875b3fe1d..0cc4dcd340e 100644 --- a/src/main/java/org/apache/sysds/hops/FunctionOp.java +++ b/src/main/java/org/apache/sysds/hops/FunctionOp.java @@ -255,7 +255,7 @@ else if ( getFunctionName().equalsIgnoreCase("eigen")) { return OptimizerUtils.estimateSizeExactSparsity(getInput().get(0).getDim1(), getInput().get(0).getDim2(), 1.0) + 3*OptimizerUtils.estimateSizeExactSparsity(getInput().get(0).getDim1(), 1, 1.0); } - if ( getFunctionName().equalsIgnoreCase("fft") ) { + else if ( getFunctionName().equalsIgnoreCase("fft") ) { // 2 matrices of size same as the input return 2*OptimizerUtils.estimateSizeExactSparsity(getInput().get(0).getDim1(), getInput().get(0).getDim2(), 1.0); } diff --git a/src/test/java/org/apache/sysds/test/component/matrix/FourierTestWithFiles.java b/src/test/java/org/apache/sysds/test/component/matrix/FourierTestWithFiles.java new file mode 100644 index 00000000000..4a4b63a897c --- /dev/null +++ b/src/test/java/org/apache/sysds/test/component/matrix/FourierTestWithFiles.java @@ -0,0 +1,378 @@ +package org.apache.sysds.test.component.matrix; + +import org.apache.sysds.runtime.matrix.data.MatrixBlock; +import org.junit.Test; + +import java.io.BufferedReader; +import java.io.FileReader; +import java.io.IOException; + +import static org.apache.sysds.runtime.matrix.data.LibMatrixFourier.*; +import static org.junit.Assert.assertTrue; + +public class FourierTestWithFiles { + int progressInterval = 100000; + + // prior to executing this test it is necessary to run the Numpy Script in FourierTestData.py and add the generated file to the root of the project. + + @Test + public void testFftWithNumpyData() throws IOException { + String filename = "fft_data.csv"; // Path to your CSV file + BufferedReader reader = new BufferedReader(new FileReader(filename)); + String line; + int lineNumber = 0; + long totalTime = 0; // Total time for all FFT computations + int numCalculations = 0; // Number of FFT computations + + while ((line = reader.readLine()) != null) { + lineNumber++; + + String[] values = line.split(","); + int n = values.length / 3; + double[][][] input = new double[2][1][n]; + double[][] expected = new double[2][n]; // First row for real, second row for imaginary parts + + for (int i = 0; i < n; i++) { + input[0][0][i] = Double.parseDouble(values[i]); + expected[0][i] = Double.parseDouble(values[n + i]); // Real part + expected[1][i] = Double.parseDouble(values[n * 2 + i]); // Imaginary part + } + + long startTime = System.nanoTime(); + MatrixBlock[] actualBlocks = fft(input); + long endTime = System.nanoTime(); + + if(lineNumber > 1000){ + totalTime += (endTime - startTime); + numCalculations++; + + if (numCalculations % progressInterval == 0) { + double averageTime = (totalTime / 1e6) / numCalculations; // Average time in milliseconds + System.out.println("fft(double[][][] in): Average execution time after " + numCalculations + " calculations: " + String.format("%.8f", averageTime/1000) + " s"); + } + } + + // Validate the FFT results + validateFftResults(expected, actualBlocks, lineNumber); + } + + reader.close(); + + } + + private void validateFftResults(double[][] expected, MatrixBlock[] actualBlocks, int lineNumber) { + int length = expected[0].length; + for (int i = 0; i < length; i++) { + double realActual = actualBlocks[0].getValueDenseUnsafe(0, i); + double imagActual = actualBlocks[1].getValueDenseUnsafe(0, i); + assertEquals("Mismatch in real part at index " + i + " in line " + lineNumber, expected[0][i], realActual, 1e-9); + assertEquals("Mismatch in imaginary part at index " + i + " in line " + lineNumber, expected[1][i], imagActual, 1e-9); + } + if(lineNumber % progressInterval == 0){ + System.out.println("fft(double[][][] in): Finished processing line " + lineNumber); + } + + } + + @Test + public void testFftExecutionTime() throws IOException { + String filename = "fft_data.csv"; // Path to your CSV file + BufferedReader reader = new BufferedReader(new FileReader(filename)); + String line; + int lineNumber = 0; + long totalTime = 0; // Total time for all FFT computations + int numCalculations = 0; // Number of FFT computations + + while ((line = reader.readLine()) != null) { + lineNumber++; + String[] values = line.split(","); + int n = values.length / 3; + double[][][] input = new double[2][1][n]; + + for (int i = 0; i < n; i++) { + input[0][0][i] = Double.parseDouble(values[i]); // Real part + input[1][0][i] = Double.parseDouble(values[n + i]); // Imaginary part + } + + long startTime = System.nanoTime(); + fft(input, false); + long endTime = System.nanoTime(); + if(lineNumber > 1000){ + totalTime += (endTime - startTime); + numCalculations++; + + if (numCalculations % progressInterval == 0) { + double averageTime = (totalTime / 1e6) / numCalculations; // Average time in milliseconds + System.out.println("fft(double[][][] in, boolean calcInv) Average execution time after " + numCalculations + " calculations: " + String.format("%.8f", averageTime/1000) + " s"); + } + } + } + + reader.close(); + } + + @Test + public void testFftExecutionTimeOfOneDimFFT() throws IOException { + String filename = "fft_data.csv"; // Path to your CSV file + BufferedReader reader = new BufferedReader(new FileReader(filename)); + String line; + int lineNumber = 0; + long totalTime = 0; // Total time for all FFT computations + int numCalculations = 0; // Number of FFT computations + + while ((line = reader.readLine()) != null) { + lineNumber++; + String[] values = line.split(","); + int n = values.length / 2; + double[][] input = new double[2][n]; // First row for real, second row for imaginary parts + + for (int i = 0; i < n; i++) { + input[0][i] = Double.parseDouble(values[i]); // Real part + input[1][i] = Double.parseDouble(values[n + i]); // Imaginary part + } + + long startTime = System.nanoTime(); + fft_one_dim(input); + long endTime = System.nanoTime(); + if(lineNumber > 1000){ + totalTime += (endTime - startTime); + numCalculations++; + + if (numCalculations % progressInterval == 0) { + double averageTime = (totalTime / 1e6) / numCalculations; // Average time in milliseconds + System.out.println("fft_one_dim: Average execution time after " + numCalculations + " calculations: " + String.format("%.8f", averageTime/1000) + " s "); + } + } + } + + reader.close(); + } + + + // prior to executing this test it is necessary to run the Numpy Script in FourierTestData.py and add the generated file to the root of the project. + @Test + public void testIfftWithRealNumpyData() throws IOException { + String filename = "ifft_data.csv"; // Path to your CSV file + BufferedReader reader = new BufferedReader(new FileReader(filename)); + String line; + int lineNumber = 0; + + while ((line = reader.readLine()) != null) { + lineNumber++; + String[] values = line.split(","); + int n = values.length / 3; + double[][][] input = new double[2][1][n]; + double[][] expected = new double[2][n]; // First row for real, second row for imaginary parts + + for (int i = 0; i < n; i++) { + input[0][0][i] = Double.parseDouble(values[i]); // Real part of input + // Imaginary part of input is assumed to be 0 + expected[0][i] = Double.parseDouble(values[n + i]); // Real part of expected output + expected[1][i] = Double.parseDouble(values[n * 2 + i]); // Imaginary part of expected output + } + + double[][][] actualResult = fft(input, true); // Perform IFFT + + // Validate the IFFT results + validateFftResults(expected, actualResult, lineNumber); + } + + reader.close(); + } + + private void validateFftResults(double[][] expected, double[][][] actualResult, int lineNumber) { + int length = expected[0].length; + for (int i = 0; i < length; i++) { + double realActual = actualResult[0][0][i]; + double imagActual = actualResult[1][0][i]; + assertEquals("Mismatch in real part at index " + i + " in line " + lineNumber, expected[0][i], realActual, 1e-9); + assertEquals("Mismatch in imaginary part at index " + i + " in line " + lineNumber, expected[1][i], imagActual, 1e-9); + } + if(lineNumber % progressInterval == 0){ + System.out.println("ifft(real input): Finished processing line " + lineNumber); + } + + } + + + + + @Test +public void testIfftWithComplexNumpyData() throws IOException { + String filename = "complex_ifft_data.csv"; // Adjusted path to your IFFT data file with complex inputs + BufferedReader reader = new BufferedReader(new FileReader(filename)); + String line; + int lineNumber = 0; + long totalTime = 0; // Total time for all IFFT computations + int numCalculations = 0; // Number of IFFT computations + + while ((line = reader.readLine()) != null) { + lineNumber++; + String[] values = line.split(","); + int n = values.length / 4; // Adjusted for complex numbers + double[][][] input = new double[2][1][n]; // Real and imaginary parts + double[][] expected = new double[2][n]; // Expected real and imaginary parts + + for (int i = 0; i < n; i++) { + input[0][0][i] = Double.parseDouble(values[i]); // Real part of input + input[1][0][i] = Double.parseDouble(values[i + n]); // Imaginary part of input + expected[0][i] = Double.parseDouble(values[i + 2 * n]); // Expected real part + expected[1][i] = Double.parseDouble(values[i + 3 * n]); // Expected imaginary part + } + + long startTime = System.nanoTime(); + double[][][] actualResult = fft(input, true); // Perform IFFT + long endTime = System.nanoTime(); + + if (lineNumber > 1000) { + totalTime += (endTime - startTime); + numCalculations++; + } + + // Validate the IFFT results + validateComplexIFftResults(expected, actualResult, lineNumber); + + if (lineNumber % progressInterval == 0) { + double averageTime = (totalTime / 1e6) / numCalculations; // Average time in milliseconds + System.out.println("ifft: Average execution time after " + numCalculations + " calculations: " + String.format("%.8f", averageTime / 1000) + " s"); + } + } + + reader.close(); +} + +private void validateComplexIFftResults(double[][] expected, double[][][] actualResult, int lineNumber) { + int length = expected[0].length; + for (int i = 0; i < length; i++) { + double realActual = actualResult[0][0][i]; + double imagActual = actualResult[1][0][i]; + assertEquals("Mismatch in real part at index " + i + " in line " + lineNumber, expected[0][i], realActual, 1e-9); + assertEquals("Mismatch in imaginary part at index " + i + " in line " + lineNumber, expected[1][i], imagActual, 1e-9); + } + if (lineNumber % progressInterval == 0) { + System.out.println("ifft(complex input): Finished processing line " + lineNumber); + } +} + + +@Test +public void testFft2dWithNumpyData() throws IOException { + String filename = "complex_fft_2d_data.csv"; // path to your 2D FFT data file + BufferedReader reader = new BufferedReader(new FileReader(filename)); + String line; + int lineNumber = 0; + long totalTime = 0; // Total time for all FFT 2D computations + int numCalculations = 0; // Number of FFT 2D computations + + while ((line = reader.readLine()) != null) { + lineNumber++; + String[] values = line.split(","); + int halfLength = values.length / 4; + int sideLength = (int) Math.sqrt(halfLength); // Assuming square matrix + + double[][][] input = new double[2][sideLength][sideLength]; + double[][][] expected = new double[2][sideLength][sideLength]; + + for (int i = 0; i < halfLength; i++) { + int row = i / sideLength; + int col = i % sideLength; + input[0][row][col] = Double.parseDouble(values[i]); + input[1][row][col] = Double.parseDouble(values[i + halfLength]); + expected[0][row][col] = Double.parseDouble(values[i + 2 * halfLength]); + expected[1][row][col] = Double.parseDouble(values[i + 3 * halfLength]); + } + + long startTime = System.nanoTime(); + double[][][] javaFftResult = fft(input, false); // Use your fft2d implementation + long endTime = System.nanoTime(); + totalTime += (endTime - startTime); + numCalculations++; + + for (int i = 0; i < sideLength; i++) { + for (int j = 0; j < sideLength; j++) { + assertComplexEquals("Mismatch at [" + i + "][" + j + "] in line " + lineNumber, + expected[0][i][j], expected[1][i][j], + javaFftResult[0][i][j], javaFftResult[1][i][j]); + } + } + + if (lineNumber % progressInterval == 0) { + double averageTime = (totalTime / 1e6) / numCalculations; // Average time in milliseconds + System.out.println("fft2d: Average execution time after " + numCalculations + " calculations: " + String.format("%.8f", averageTime/1000) + " s"); + } + } + + reader.close(); + System.out.println("fft2d: Finished processing " + lineNumber + " lines.\n Average execution time: " + String.format("%.8f", (totalTime / 1e6 / numCalculations)/1000) + " s"); +} + + +@Test +public void testIfft2dWithNumpyData() throws IOException { + String filename = "complex_ifft_2d_data.csv"; // path to your 2D IFFT data file + BufferedReader reader = new BufferedReader(new FileReader(filename)); + String line; + int lineNumber = 0; + long totalTime = 0; // Total time for all IFFT 2D computations + int numCalculations = 0; // Number of IFFT 2D computations + + while ((line = reader.readLine()) != null) { + lineNumber++; + String[] values = line.split(","); + int halfLength = values.length / 4; + int sideLength = (int) Math.sqrt(halfLength); // Assuming square matrix + + double[][][] input = new double[2][sideLength][sideLength]; + double[][][] expected = new double[2][sideLength][sideLength]; + + for (int i = 0; i < halfLength; i++) { + int row = i / sideLength; + int col = i % sideLength; + input[0][row][col] = Double.parseDouble(values[i]); + input[1][row][col] = Double.parseDouble(values[i + halfLength]); + expected[0][row][col] = Double.parseDouble(values[i + 2 * halfLength]); + expected[1][row][col] = Double.parseDouble(values[i + 3 * halfLength]); + } + + long startTime = System.nanoTime(); + double[][][] javaIfftResult = fft(input, true); // Use your ifft2d implementation + long endTime = System.nanoTime(); + if(lineNumber > 1000){ + totalTime += (endTime - startTime); + numCalculations++; + } + + for (int i = 0; i < sideLength; i++) { + for (int j = 0; j < sideLength; j++) { + assertComplexEquals("Mismatch at [" + i + "][" + j + "] in line " + lineNumber, + expected[0][i][j], expected[1][i][j], + javaIfftResult[0][i][j], javaIfftResult[1][i][j]); + } + } + + if (lineNumber % progressInterval == 0) { + System.out.println("fft2d/ifft2d: Finished processing line " + lineNumber); + double averageTime = (totalTime / 1e6) / numCalculations; // Average time in milliseconds + System.out.println("Ifft2d Average execution time after " + numCalculations + " calculations: " + String.format("%.8f", averageTime/1000) + " s"); + } + } + + reader.close(); + System.out.println("ifft2d: Finished processing " + lineNumber + " lines.\n Average execution time: " + String.format("%.8f", (totalTime / 1e6 / numCalculations)/1000) + " ms"); +} + + // Helper method for asserting equality with a tolerance + private static void assertEquals(String message, double expected, double actual, double tolerance) { + assertTrue(message + " - Expected: " + expected + ", Actual: " + actual, Math.abs(expected - actual) <= tolerance); + } + + private void assertComplexEquals(String message, double expectedReal, double expectedImag, double actualReal, double actualImag) { + final double EPSILON = 1e-9; + assertTrue(message + " - Mismatch in real part. Expected: " + expectedReal + ", Actual: " + actualReal, + Math.abs(expectedReal - actualReal) <= EPSILON); + assertTrue(message + " - Mismatch in imaginary part. Expected: " + expectedImag + ", Actual: " + actualImag, + Math.abs(expectedImag - actualImag) <= EPSILON); + } + + +} \ No newline at end of file From b8286684986d26682e8342a28d2cd1821308d11a Mon Sep 17 00:00:00 2001 From: mufwan <105200913+mufwan@users.noreply.github.com> Date: Sun, 14 Jan 2024 16:07:33 +0100 Subject: [PATCH 023/133] Delete src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier2.java --- .../matrix/data/LibMatrixFourier2.java | 190 ------------------ 1 file changed, 190 deletions(-) delete mode 100644 src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier2.java diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier2.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier2.java deleted file mode 100644 index 8f347ad4244..00000000000 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier2.java +++ /dev/null @@ -1,190 +0,0 @@ -package org.apache.sysds.runtime.matrix.data; - -public class LibMatrixFourier2 { - - - public static ComplexDouble[] fft(double[] in){ - ComplexDouble[] complex = new ComplexDouble[in.length]; - double[] im = new double[in.length]; - transform(in, im); - for(int i=0; i>> (32 - levels); - if (j > i) { - double temp = real[i]; - real[i] = real[j]; - real[j] = temp; - temp = imag[i]; - imag[i] = imag[j]; - imag[j] = temp; - } - } - - // Cooley-Tukey decimation-in-time radix-2 FFT - for (int size = 2; size <= n; size *= 2) { - int halfsize = size / 2; - int tablestep = n / size; - for (int i = 0; i < n; i += size) { - for (int j = i, k = 0; j < i + halfsize; j++, k += tablestep) { - int l = j + halfsize; - double tpre = real[l] * cosTable[k] + imag[l] * sinTable[k]; - double tpim = -real[l] * sinTable[k] + imag[l] * cosTable[k]; - real[l] = real[j] - tpre; - imag[l] = imag[j] - tpim; - real[j] += tpre; - imag[j] += tpim; - } - } - if (size == n) // Prevent overflow in 'size *= 2' - break; - } - } - - - /* - * Computes the discrete Fourier transform (DFT) of the given complex vector, storing the result back into the vector. - * The vector can have any length. This requires the convolution function, which in turn requires the radix-2 FFT function. - * Uses Bluestein's chirp z-transform algorithm. - */ - public static void transformBluestein(double[] real, double[] imag) { - // Find a power-of-2 convolution length m such that m >= n * 2 + 1 - int n = real.length; - if (n != imag.length) - throw new IllegalArgumentException("Mismatched lengths"); - if (n >= 0x20000000) - throw new IllegalArgumentException("Array too large"); - int m = Integer.highestOneBit(n) * 4; - - // Trigonometric tables - double[] cosTable = new double[n]; - double[] sinTable = new double[n]; - for (int i = 0; i < n; i++) { - int j = (int)((long)i * i % (n * 2)); // This is more accurate than j = i * i - cosTable[i] = Math.cos(Math.PI * j / n); - sinTable[i] = Math.sin(Math.PI * j / n); - } - - // Temporary vectors and preprocessing - double[] areal = new double[m]; - double[] aimag = new double[m]; - for (int i = 0; i < n; i++) { - areal[i] = real[i] * cosTable[i] + imag[i] * sinTable[i]; - aimag[i] = -real[i] * sinTable[i] + imag[i] * cosTable[i]; - } - double[] breal = new double[m]; - double[] bimag = new double[m]; - breal[0] = cosTable[0]; - bimag[0] = sinTable[0]; - for (int i = 1; i < n; i++) { - breal[i] = breal[m - i] = cosTable[i]; - bimag[i] = bimag[m - i] = sinTable[i]; - } - - // Convolution - double[] creal = new double[m]; - double[] cimag = new double[m]; - convolve(areal, aimag, breal, bimag, creal, cimag); - - // Postprocessing - for (int i = 0; i < n; i++) { - real[i] = creal[i] * cosTable[i] + cimag[i] * sinTable[i]; - imag[i] = -creal[i] * sinTable[i] + cimag[i] * cosTable[i]; - } - } - - - /* - * Computes the circular convolution of the given real vectors. Each vector's length must be the same. - */ - public static void convolve(double[] xvec, double[] yvec, double[] outvec) { - int n = xvec.length; - if (n != yvec.length || n != outvec.length) - throw new IllegalArgumentException("Mismatched lengths"); - convolve(xvec, new double[n], yvec, new double[n], outvec, new double[n]); - } - - - /* - * Computes the circular convolution of the given complex vectors. Each vector's length must be the same. - */ - public static void convolve(double[] xreal, double[] ximag, - double[] yreal, double[] yimag, double[] outreal, double[] outimag) { - - int n = xreal.length; - if (n != ximag.length || n != yreal.length || n != yimag.length - || n != outreal.length || n != outimag.length) - throw new IllegalArgumentException("Mismatched lengths"); - - xreal = xreal.clone(); - ximag = ximag.clone(); - yreal = yreal.clone(); - yimag = yimag.clone(); - transform(xreal, ximag); - transform(yreal, yimag); - - for (int i = 0; i < n; i++) { - double temp = xreal[i] * yreal[i] - ximag[i] * yimag[i]; - ximag[i] = ximag[i] * yreal[i] + xreal[i] * yimag[i]; - xreal[i] = temp; - } - inverseTransform(xreal, ximag); - - for (int i = 0; i < n; i++) { // Scaling (because this FFT implementation omits it) - outreal[i] = xreal[i] / n; - outimag[i] = ximag[i] / n; - } - } - -} From 8335fb7501ccfbebc9aa2071e86c25178caa6761 Mon Sep 17 00:00:00 2001 From: mufwan <105200913+mufwan@users.noreply.github.com> Date: Sun, 14 Jan 2024 16:08:21 +0100 Subject: [PATCH 024/133] Delete src/test/java/org/apache/sysds/test/component/matrix/Fourier2Test.java --- .../test/component/matrix/Fourier2Test.java | 58 ------------------- 1 file changed, 58 deletions(-) delete mode 100644 src/test/java/org/apache/sysds/test/component/matrix/Fourier2Test.java diff --git a/src/test/java/org/apache/sysds/test/component/matrix/Fourier2Test.java b/src/test/java/org/apache/sysds/test/component/matrix/Fourier2Test.java deleted file mode 100644 index 2df6e2c23fe..00000000000 --- a/src/test/java/org/apache/sysds/test/component/matrix/Fourier2Test.java +++ /dev/null @@ -1,58 +0,0 @@ -package org.apache.sysds.test.component.matrix; - -import org.apache.sysds.runtime.matrix.data.ComplexDouble; -import org.apache.sysds.runtime.matrix.data.LibMatrixFourier; -import org.apache.sysds.runtime.matrix.data.LibMatrixFourier2; - -import static org.apache.sysds.runtime.matrix.data.LibMatrixFourier2.fft; -import static org.apache.sysds.runtime.matrix.data.LibMatrixFourier2.transform; - -import org.junit.Test; - -import java.util.Arrays; - -import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertTrue; - - -public class Fourier2Test { - - @Test - public void simpleTest() { - // tested with numpy - double[] in = {0, 18, -15, 3}; - ComplexDouble[] expected = new ComplexDouble[4]; - expected[0] = new ComplexDouble(6, 0); - expected[1] = new ComplexDouble(15, -15); - expected[2] = new ComplexDouble(-36, 0); - expected[3] = new ComplexDouble(15, 15); - - ComplexDouble[] res = fft(in); - for(ComplexDouble elem : res){ - System.out.println(elem); - } - - assertArrayEquals(expected, res); - } - - @Test - public void notPowerOfTwoTest() { - double[] in = {1, 2, 3, 4, 5}; - - // see https://de.mathworks.com/help/matlab/ref/ifft.html - ComplexDouble[] expected = new ComplexDouble[5]; - expected[0] = new ComplexDouble(15,0); - expected[1] = new ComplexDouble(-2.5000,3.4410); - expected[2] = new ComplexDouble(-2.5000,0.8123); - expected[3] = new ComplexDouble(-2.5000, - 0.8123); - expected[4] = new ComplexDouble(-2.5000, - 3.4410); - - ComplexDouble[] res = fft(in); - for(ComplexDouble elem : res){ - System.out.println(elem); - } - assertArrayEquals(expected, res); - } - - -} From a8ae4489d2c9d582e9cfa0a011a9ed664073a279 Mon Sep 17 00:00:00 2001 From: Frederic Zoepffel Date: Mon, 15 Jan 2024 15:22:24 +0100 Subject: [PATCH 025/133] ifft integration in dml --- .../org/apache/sysds/common/Builtins.java | 1 + .../org/apache/sysds/hops/FunctionOp.java | 9 ++++++ .../parser/BuiltinFunctionExpression.java | 28 +++++++++++++++++++ .../apache/sysds/parser/DMLTranslator.java | 1 + .../instructions/CPInstructionParser.java | 1 + .../cp/MultiReturnBuiltinCPInstruction.java | 8 ++++++ .../runtime/matrix/data/LibCommonsMath.java | 22 +++++++++++++-- .../test/component/matrix/Fourier2Test.java | 8 ------ .../matrix/FourierTestWithFiles.java | 7 ++--- 9 files changed, 71 insertions(+), 14 deletions(-) diff --git a/src/main/java/org/apache/sysds/common/Builtins.java b/src/main/java/org/apache/sysds/common/Builtins.java index 9366acebf72..21f75c2811e 100644 --- a/src/main/java/org/apache/sysds/common/Builtins.java +++ b/src/main/java/org/apache/sysds/common/Builtins.java @@ -155,6 +155,7 @@ public enum Builtins { HOSPITAL_RESIDENCY_MATCH("hospitalResidencyMatch", true), HYPERBAND("hyperband", true), IFELSE("ifelse", false), + IFFT("fft", false, ReturnType.MULTI_RETURN), IMG_MIRROR("img_mirror", true), IMG_MIRROR_LINEARIZED("img_mirror_linearized", true), IMG_BRIGHTNESS("img_brightness", true), diff --git a/src/main/java/org/apache/sysds/hops/FunctionOp.java b/src/main/java/org/apache/sysds/hops/FunctionOp.java index 0cc4dcd340e..4d439bf1001 100644 --- a/src/main/java/org/apache/sysds/hops/FunctionOp.java +++ b/src/main/java/org/apache/sysds/hops/FunctionOp.java @@ -206,6 +206,11 @@ else if ( getFunctionName().equalsIgnoreCase("fft") ) { long outputIm = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(1).getDim1(), getOutputs().get(1).getDim2(), 1.0); return outputRe+outputIm; } + else if ( getFunctionName().equalsIgnoreCase("ifft") ) { + long outputRe = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(0).getDim1(), getOutputs().get(0).getDim2(), 1.0); + long outputIm = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(1).getDim1(), getOutputs().get(1).getDim2(), 1.0); + return outputRe+outputIm; + } else if ( getFunctionName().equalsIgnoreCase("lstm") || getFunctionName().equalsIgnoreCase("lstm_backward") ) { // TODO: To allow for initial version to always run on the GPU return 0; @@ -259,6 +264,10 @@ else if ( getFunctionName().equalsIgnoreCase("fft") ) { // 2 matrices of size same as the input return 2*OptimizerUtils.estimateSizeExactSparsity(getInput().get(0).getDim1(), getInput().get(0).getDim2(), 1.0); } + else if ( getFunctionName().equalsIgnoreCase("ifft") ) { + // 2 matrices of size same as the input + return 2*OptimizerUtils.estimateSizeExactSparsity(getInput().get(0).getDim1(), getInput().get(0).getDim2(), 1.0); + } else if (getFunctionName().equalsIgnoreCase("batch_norm2d") || getFunctionName().equalsIgnoreCase("batch_norm2d_backward") || getFunctionName().equalsIgnoreCase("batch_norm2d_train") || getFunctionName().equalsIgnoreCase("batch_norm2d_test")) { return 0; diff --git a/src/main/java/org/apache/sysds/parser/BuiltinFunctionExpression.java b/src/main/java/org/apache/sysds/parser/BuiltinFunctionExpression.java index 7c682175996..638db06226b 100644 --- a/src/main/java/org/apache/sysds/parser/BuiltinFunctionExpression.java +++ b/src/main/java/org/apache/sysds/parser/BuiltinFunctionExpression.java @@ -382,6 +382,34 @@ public void validateExpression(MultiAssignmentStatement stmt, HashMap Date: Mon, 15 Jan 2024 15:46:24 +0100 Subject: [PATCH 026/133] dml integration ifft --- .../org/apache/sysds/common/Builtins.java | 2 +- .../parser/BuiltinFunctionExpression.java | 2 +- .../runtime/matrix/data/LibCommonsMath.java | 21 +++++++------------ 3 files changed, 10 insertions(+), 15 deletions(-) diff --git a/src/main/java/org/apache/sysds/common/Builtins.java b/src/main/java/org/apache/sysds/common/Builtins.java index 21f75c2811e..36655be0590 100644 --- a/src/main/java/org/apache/sysds/common/Builtins.java +++ b/src/main/java/org/apache/sysds/common/Builtins.java @@ -155,7 +155,7 @@ public enum Builtins { HOSPITAL_RESIDENCY_MATCH("hospitalResidencyMatch", true), HYPERBAND("hyperband", true), IFELSE("ifelse", false), - IFFT("fft", false, ReturnType.MULTI_RETURN), + IFFT("ifft", false, ReturnType.MULTI_RETURN), IMG_MIRROR("img_mirror", true), IMG_MIRROR_LINEARIZED("img_mirror_linearized", true), IMG_BRIGHTNESS("img_brightness", true), diff --git a/src/main/java/org/apache/sysds/parser/BuiltinFunctionExpression.java b/src/main/java/org/apache/sysds/parser/BuiltinFunctionExpression.java index 638db06226b..275e65975bc 100644 --- a/src/main/java/org/apache/sysds/parser/BuiltinFunctionExpression.java +++ b/src/main/java/org/apache/sysds/parser/BuiltinFunctionExpression.java @@ -385,7 +385,7 @@ public void validateExpression(MultiAssignmentStatement stmt, HashMap Date: Mon, 15 Jan 2024 16:18:44 +0100 Subject: [PATCH 027/133] removed complexDouble --- .../runtime/matrix/data/ComplexDouble.java | 66 --- .../data/LibMatrixFourierComplexDouble.java | 220 ------- .../matrix/FourierComplexDoubleTest.java | 536 ------------------ 3 files changed, 822 deletions(-) delete mode 100644 src/main/java/org/apache/sysds/runtime/matrix/data/ComplexDouble.java delete mode 100644 src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourierComplexDouble.java delete mode 100644 src/test/java/org/apache/sysds/test/component/matrix/FourierComplexDoubleTest.java diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/ComplexDouble.java b/src/main/java/org/apache/sysds/runtime/matrix/data/ComplexDouble.java deleted file mode 100644 index 668c61690b8..00000000000 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/ComplexDouble.java +++ /dev/null @@ -1,66 +0,0 @@ -package org.apache.sysds.runtime.matrix.data; - -public class ComplexDouble { - public double re; - public double im; - - public ComplexDouble(double re, double im){ - this.re = re; - this.im = im; - } - - public ComplexDouble add(ComplexDouble other){ - return new ComplexDouble(this.re + other.re, this.im + other.im); - } - - public ComplexDouble sub(ComplexDouble other){ - return new ComplexDouble(this.re - other.re, this.im - other.im); - } - public ComplexDouble mul(ComplexDouble other){ - return new ComplexDouble(this.re * other.re - this.im * other.im, this.im * other.re + this.re * other.im); - } - - // Division - public ComplexDouble div(ComplexDouble other) { - double denominator = other.re * other.re + other.im * other.im; - return new ComplexDouble((this.re * other.re + this.im * other.im) / denominator, - (this.im * other.re - this.re * other.im) / denominator); - } - - // Conjugate - public ComplexDouble conjugate() { - return new ComplexDouble(this.re, -this.im); - } - - // Absolute Value - public double abs() { - return Math.sqrt(this.re * this.re + this.im * this.im); - } - - // Argument (Phase) - public double arg() { - return Math.atan2(this.im, this.re); - } - - // Polar Form Conversion - public static ComplexDouble fromPolar(double magnitude, double angle) { - return new ComplexDouble(magnitude * Math.cos(angle), magnitude * Math.sin(angle)); - } - - - @Override - public String toString() { - return this.re + " + " + this.im + "i"; - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - ComplexDouble that = (ComplexDouble) o; - - double epsilon = 0.000001d; - return Math.abs(this.re - that.re) < epsilon && Math.abs(this.im - that.im) < epsilon; - } - -} diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourierComplexDouble.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourierComplexDouble.java deleted file mode 100644 index f17d894b65a..00000000000 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourierComplexDouble.java +++ /dev/null @@ -1,220 +0,0 @@ -package org.apache.sysds.runtime.matrix.data; - -public class LibMatrixFourierComplexDouble { - - /** - * Function to perform Fast Fourier Transformation on a given array. - * Its length has to be a power of two. - * - * @param in array of doubles - * @return array of ComplexDoubles - */ - public static ComplexDouble[] fft_old(double[] in){ - ComplexDouble[] complex = new ComplexDouble[in.length]; - for(int i=0; i 1000){ - totalTime += (endTime - startTime); - numCalculations++; - - - - if (numCalculations % 5000 == 0) { - double averageTime = (totalTime / 1e6) / numCalculations; // Average time in milliseconds - System.out.println("\nAverage execution time after " + numCalculations + " calculations: " + String.format("%.8f", averageTime/1000) + " s \n"); - } - } - } - - reader.close(); - System.out.println("Finished processing " + lineNumber + " lines."); - } - - - - // prior to executing this test it is necessary to run the Numpy Script in FourierTestData.py and add the generated file to the root of the project. - @Test - public void testIfftWithRealNumpyData() throws IOException { - String filename = "ifft_data.csv"; // path to your IFFT data file - BufferedReader reader = new BufferedReader(new FileReader(filename)); - String line; - int lineNumber = 0; - long totalTime = 0; // Total time for all IFFT computations - int numCalculations = 0; // Number of IFFT computations - - while ((line = reader.readLine()) != null) { - lineNumber++; - String[] values = line.split(","); - int n = values.length / 3; - ComplexDouble[] originalInput = new ComplexDouble[n]; - ComplexDouble[] numpyIfftResult = new ComplexDouble[n]; - ComplexDouble[] javaIfftResult; - - for (int i = 0; i < n; i++) { - originalInput[i] = new ComplexDouble(Double.parseDouble(values[i]), 0); // Original data - double realPart = Double.parseDouble(values[n + i]); - double imagPart = Double.parseDouble(values[n * 2 + i]); - numpyIfftResult[i] = new ComplexDouble(realPart, imagPart); // NumPy IFFT result - } - - long startTime = System.nanoTime(); - javaIfftResult = ifft(originalInput); - long endTime = System.nanoTime(); - if(lineNumber > 1000){ - totalTime += (endTime - startTime); - numCalculations++; - } - for (int i = 0; i < n; i++) { - assertComplexEquals("Mismatch at index " + i + " in line " + lineNumber, numpyIfftResult[i], javaIfftResult[i]); - } - - if (numCalculations % 5000 == 0) { - double averageTime = (totalTime / 1e6) / numCalculations; // Average time in milliseconds - System.out.println("Average execution time after " + numCalculations + " calculations: " + String.format("%.8f", averageTime/1000) + " s"); - // System.out.println("input: "); - // for(int i = 0; i < originalInput.length; i++ ){ - // System.out.println(originalInput[i].toString()); - // } - // System.out.println("output: " ); - // for(int i = 0; i < javaIfftResult.length; i++ ){ - // System.out.println(javaIfftResult[i].toString()); - // } - } - } - - reader.close(); - System.out.println("Finished processing " + lineNumber + " lines."); - } - - @Test - public void testIfftWithComplexNumpyData() throws IOException { - String filename = "complex_ifft_data.csv"; // Adjusted path to your IFFT data file with complex inputs - BufferedReader reader = new BufferedReader(new FileReader(filename)); - String line; - int lineNumber = 0; - long totalTime = 0; // Total time for all IFFT computations - int numCalculations = 0; // Number of IFFT computations - - while ((line = reader.readLine()) != null) { - lineNumber++; - String[] values = line.split(","); - int n = values.length / 4; // Adjusted for complex numbers - ComplexDouble[] originalInput = new ComplexDouble[n]; - ComplexDouble[] numpyIfftResult = new ComplexDouble[n]; - ComplexDouble[] javaIfftResult; - - for (int i = 0; i < n; i++) { - double realPartOriginal = Double.parseDouble(values[i]); - double imagPartOriginal = Double.parseDouble(values[i + n]); - originalInput[i] = new ComplexDouble(realPartOriginal, imagPartOriginal); // Original complex data - - double realPartIfft = Double.parseDouble(values[i + 2 * n]); - double imagPartIfft = Double.parseDouble(values[i + 3 * n]); - numpyIfftResult[i] = new ComplexDouble(realPartIfft, imagPartIfft); // NumPy IFFT result - } - - long startTime = System.nanoTime(); - javaIfftResult = ifft(originalInput); - long endTime = System.nanoTime(); - - if(lineNumber > 1000){ - totalTime += (endTime - startTime); - numCalculations++; - } - for (int i = 0; i < n; i++) { - assertComplexEquals("Mismatch at index " + i + " in line " + lineNumber, numpyIfftResult[i], javaIfftResult[i]); - } - - if (numCalculations % 5000 == 0) { - double averageTime = (totalTime / 1e6) / numCalculations; // Average time in milliseconds - System.out.println("Average execution time after " + numCalculations + " calculations: " + String.format("%.8f", averageTime/1000) + " s"); - // System.out.println("input: "); - // for(int i = 0; i < originalInput.length; i++ ){ - // System.out.println(originalInput[i].toString()); - // } - // System.out.println("output: " ); - // for(int i = 0; i < javaIfftResult.length; i++ ){ - // System.out.println(javaIfftResult[i].toString()); - // } - - } - } - - reader.close(); - System.out.println("Finished processing " + lineNumber + " lines."); - } - - - - // prior to executing this test it is necessary to run the Numpy Script in FourierTestData.py and add the generated file to the root of the project. - @Test - public void testIfftExecutionTime() throws IOException { - String filename = "ifft_data.csv"; // path to your IFFT data file - BufferedReader reader = new BufferedReader(new FileReader(filename)); - String line; - int lineNumber = 0; - long totalTime = 0; // Total time for all IFFT computations - int numCalculations = 0; // Number of IFFT computations - - while ((line = reader.readLine()) != null) { - lineNumber++; - String[] values = line.split(","); - int n = values.length / 3; - ComplexDouble[] original = new ComplexDouble[n]; - ComplexDouble[] numpyResult = new ComplexDouble[n]; - - for (int i = 0; i < n; i++) { - original[i] = new ComplexDouble(Double.parseDouble(values[i]), 0); - double realPart = Double.parseDouble(values[n + i]); - double imagPart = Double.parseDouble(values[n * 2 + i]); - numpyResult[i] = new ComplexDouble(realPart, imagPart); - } - - long startTime = System.nanoTime(); - ifft(numpyResult); - long endTime = System.nanoTime(); - if(lineNumber > 5000){ - totalTime += (endTime - startTime); - numCalculations++; - - - if (numCalculations % 1000 == 0) { - double averageTime = (totalTime / 1e6) / numCalculations; // Average time in milliseconds - System.out.println("Average execution time after " + numCalculations + " calculations: " + String.format("%.8f", averageTime/1000) + " s"); - } - } - } - - reader.close(); - System.out.println("Finished processing " + lineNumber + " lines."); - } - - - @Test - public void testSimpleIfft2d() { - double[][] original = {{1, -2}, {3, -4}}; - ComplexDouble[][] complexInput = new ComplexDouble[original.length][original[0].length]; - for (int i = 0; i < original.length; i++) { - for (int j = 0; j < original[0].length; j++) { - complexInput[i][j] = new ComplexDouble(original[i][j], 0); - } - } - - ComplexDouble[][] fftResult = fft2d(complexInput); - ComplexDouble[][] ifftResult = ifft2d(fftResult); - - for (int i = 0; i < original.length; i++) { - for (int j = 0; j < original[0].length; j++) { - assertEquals("Mismatch at [" + i + "][" + j + "]", original[i][j], ifftResult[i][j].re, 0.000001); - assertEquals("Non-zero imaginary part at [" + i + "][" + j + "]", 0, ifftResult[i][j].im, 0.000001); - } - } - } - - - @Test - public void testFft2dWithNumpyData() throws IOException { - String filename = "complex_fft_2d_data.csv"; // path to your 2D FFT data file - BufferedReader reader = new BufferedReader(new FileReader(filename)); - String line; - int lineNumber = 0; - long totalTime = 0; // Total time for all FFT 2D computations - int numCalculations = 0; // Number of FFT 2D computations - int progressInterval = 1000; // Print progress every 1000 lines - - while ((line = reader.readLine()) != null) { - lineNumber++; - String[] values = line.split(","); - int halfLength = values.length / 4; - int sideLength = (int) Math.sqrt(halfLength); // Assuming square matrix - - ComplexDouble[][] originalInput = new ComplexDouble[sideLength][sideLength]; - ComplexDouble[][] numpyFftResult = new ComplexDouble[sideLength][sideLength]; - - for (int i = 0; i < halfLength; i++) { - int row = i / sideLength; - int col = i % sideLength; - double realPartOriginal = Double.parseDouble(values[i]); - double imagPartOriginal = Double.parseDouble(values[i + halfLength]); - originalInput[row][col] = new ComplexDouble(realPartOriginal, imagPartOriginal); - - double realPartFft = Double.parseDouble(values[i + 2 * halfLength]); - double imagPartFft = Double.parseDouble(values[i + 3 * halfLength]); - numpyFftResult[row][col] = new ComplexDouble(realPartFft, imagPartFft); - } - - long startTime = System.nanoTime(); - ComplexDouble[][] javaFftResult = fft2d(originalInput); - long endTime = System.nanoTime(); - totalTime += (endTime - startTime); - numCalculations++; - - for (int i = 0; i < sideLength; i++) { - for (int j = 0; j < sideLength; j++) { - assertComplexEquals("Mismatch at [" + i + "][" + j + "] in line " + lineNumber, numpyFftResult[i][j], javaFftResult[i][j]); - } - } - - if (lineNumber % progressInterval == 0) { // Print progress - System.out.println("Processing line: " + lineNumber); - if (numCalculations > 0) { - double averageTime = (totalTime / 1e6) / numCalculations; // Average time in milliseconds - System.out.println("Average execution time after " + numCalculations + " calculations: " + String.format("%.8f", averageTime/1000) + " s"); - } - } - } - - reader.close(); - System.out.println("Finished processing " + lineNumber + " lines. Average execution time: " + String.format("%.8f", (totalTime / 1e6 / numCalculations)/1000) + " s"); - } - - @Test - public void testIfft2dWithNumpyData() throws IOException { - String filename = "complex_ifft_2d_data.csv"; // path to your 2D IFFT data file - BufferedReader reader = new BufferedReader(new FileReader(filename)); - String line; - int lineNumber = 0; - long totalTime = 0; // Total time for all IFFT 2D computations - int numCalculations = 0; // Number of IFFT 2D computations - int progressInterval = 10000; // Print progress every 1000 lines - - while ((line = reader.readLine()) != null) { - lineNumber++; - String[] values = line.split(","); - int halfLength = values.length / 4; - int sideLength = (int) Math.sqrt(halfLength); // Assuming square matrix - - ComplexDouble[][] originalInput = new ComplexDouble[sideLength][sideLength]; - ComplexDouble[][] numpyIfftResult = new ComplexDouble[sideLength][sideLength]; - - for (int i = 0; i < halfLength; i++) { - int row = i / sideLength; - int col = i % sideLength; - double realPartOriginal = Double.parseDouble(values[i]); - double imagPartOriginal = Double.parseDouble(values[i + halfLength]); - originalInput[row][col] = new ComplexDouble(realPartOriginal, imagPartOriginal); - - double realPartIfft = Double.parseDouble(values[i + 2 * halfLength]); - double imagPartIfft = Double.parseDouble(values[i + 3 * halfLength]); - numpyIfftResult[row][col] = new ComplexDouble(realPartIfft, imagPartIfft); - } - - long startTime = System.nanoTime(); - ComplexDouble[][] javaIfftResult = ifft2d(originalInput); - long endTime = System.nanoTime(); - if(lineNumber > 1000){ - totalTime += (endTime - startTime); - numCalculations++; - } - - for (int i = 0; i < sideLength; i++) { - for (int j = 0; j < sideLength; j++) { - assertComplexEquals("Mismatch at [" + i + "][" + j + "] in line " + lineNumber, numpyIfftResult[i][j], javaIfftResult[i][j]); - } - } - - if (lineNumber % progressInterval == 0) { // Print progress - System.out.println("Processing line: " + lineNumber); - if (numCalculations > 0) { - double averageTime = (totalTime / 1e6) / numCalculations; // Average time in milliseconds - System.out.println("Average execution time after " + numCalculations + " calculations: " + String.format("%.8f", averageTime/1000) + " s"); - } - } - } - - reader.close(); - System.out.println("Finished processing " + lineNumber + " lines. Average execution time: " + String.format("%.8f", (totalTime / 1e6 / numCalculations)/1000) + " ms"); - } - - // Helper method for asserting equality with a tolerance - private static void assertEquals(String message, double expected, double actual, double tolerance) { - assertTrue(message + " - Expected: " + expected + ", Actual: " + actual, Math.abs(expected - actual) <= tolerance); - } - - private void assertComplexEquals(String message, ComplexDouble expected, ComplexDouble actual) { - final double EPSILON = 0.000001; - boolean realMatch = Math.abs(Math.abs(expected.re) - Math.abs(actual.re)) < EPSILON; - boolean imagMatch = Math.abs(Math.abs(expected.im) - Math.abs(actual.im)) < EPSILON; - - if (realMatch && imagMatch) { - if (Math.signum(expected.re) != Math.signum(actual.re)) { - System.out.println(message + " - Real part is of opposite sign but otherwise correct"); - } - if (Math.signum(expected.im) != Math.signum(actual.im)) { - System.out.println(message + " - Imaginary part is of opposite sign but otherwise correct"); - } - } else { - assertTrue(message + " - Incorrect values", false); - } - } - - @Test - public void simpleTest() { - // tested with numpy - double[] in = {0, 18, -15, 3}; - ComplexDouble[] expected = new ComplexDouble[4]; - expected[0] = new ComplexDouble(6, 0); - expected[1] = new ComplexDouble(15, -15); - expected[2] = new ComplexDouble(-36, 0); - expected[3] = new ComplexDouble(15, 15); - - ComplexDouble[] res = fft_old(in); - for(ComplexDouble elem : res){ - System.out.println(elem); - } - - assertArrayEquals(expected, res); - } - - @Test - public void notPowerOfTwoTest() { - double[] in = {1, 2, 3, 4, 5}; - - // see https://de.mathworks.com/help/matlab/ref/ifft.html - ComplexDouble[] expected = new ComplexDouble[5]; - expected[0] = new ComplexDouble(15,0); - expected[1] = new ComplexDouble(-2.5000,3.4410); - expected[2] = new ComplexDouble(-2.5000,0.8123); - expected[3] = new ComplexDouble(-2.5000, - 0.8123); - expected[4] = new ComplexDouble(-2.5000, - 3.4410); - - ComplexDouble[] res = fft_old(in); - for(ComplexDouble elem : res){ - System.out.println(elem); - } - assertArrayEquals(expected, res); - } - - @Test - public void simple2dTest() { - // tested with matlab - double[][] in = {{0, 18}, {-15, 3}}; - ComplexDouble[][] expected = new ComplexDouble[2][2]; - expected[0][0] = new ComplexDouble(6, 0); - expected[0][1] = new ComplexDouble(-36, 0); - expected[1][0] = new ComplexDouble(30, 0); - expected[1][1] = new ComplexDouble(0, 0); - - ComplexDouble[][] res = fft2d(in); - for(ComplexDouble[] row : res){ - for(ComplexDouble elem : row){ - System.out.println(elem); - } - } - - assertArrayEquals(expected, res); - } - - @Test - public void simple2dTest2() { - double[][] in = {{0, 18, -15, 3}, {0, 18, -15, 3}}; - ComplexDouble[][] expected = new ComplexDouble[2][4]; - expected[0][0] = new ComplexDouble(12, 0); - expected[0][1] = new ComplexDouble(30, -30); - expected[0][2] = new ComplexDouble(-72, 0); - expected[0][3] = new ComplexDouble(30, 30); - expected[1][0] = new ComplexDouble(0, 0); - expected[1][1] = new ComplexDouble(0, 0); - expected[1][2] = new ComplexDouble(0, 0); - expected[1][3] = new ComplexDouble(0, 0); - - ComplexDouble[][] res = fft2d(in); - for(ComplexDouble[] row : res){ - for(ComplexDouble elem : row){ - System.out.println(elem); - } - } - - assertArrayEquals(expected, res); - } - - @Test - public void simple2dTest2SecondPart() { - // ComplexDouble(15, -15) is the second (expected[1]) entry of simpleTest - // this tests the col computation in fft2d for simple2dTest2 - - ComplexDouble[] in = new ComplexDouble[2]; - in[0] = new ComplexDouble(15, -15); - in[1] = new ComplexDouble(15, -15); - - ComplexDouble[] expected = new ComplexDouble[2]; - expected[0] = new ComplexDouble(30, -30); - expected[1] = new ComplexDouble(0, 0); - - ComplexDouble[] res = fft(in); - for (ComplexDouble elem : res) { - System.out.println(elem); - } - - assertArrayEquals(expected, res); - } - - - - @Test - public void testSimpleIfft1d() { - double[] original = {1, -2, 3, -4}; - ComplexDouble[] complexInput = new ComplexDouble[original.length]; - for (int i = 0; i < original.length; i++) { - complexInput[i] = new ComplexDouble(original[i], 0); - } - - ComplexDouble[] fftResult = fft(complexInput); - ComplexDouble[] ifftResult = ifft(fftResult); - - for (int i = 0; i < original.length; i++) { - assertEquals("Mismatch at index " + i, original[i], ifftResult[i].re, 0.000001); - assertEquals("Non-zero imaginary part at index " + i, 0, ifftResult[i].im, 0.000001); - } - } - -} From 42faba11a6b32c87d616b201ed8baedbf91f8cf0 Mon Sep 17 00:00:00 2001 From: Jessica Priebe Date: Mon, 15 Jan 2024 16:53:22 +0100 Subject: [PATCH 028/133] beautified --- .../runtime/matrix/data/LibMatrixFourier.java | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java index 1c9fee6642e..f622af3379e 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java @@ -46,12 +46,8 @@ public static double[][][] fft(double[][][] in, boolean calcInv){ for(int i = 0; i < rows; i++){ // use fft or ifft on each row - double[][] res_row; - if(calcInv){ - res_row = ifft_one_dim(get_complex_row(in, i)); - } else { - res_row = fft_one_dim(get_complex_row(in, i)); - } + double[][] res_row = calcInv? ifft_one_dim(get_complex_row(in, i)) : fft_one_dim(get_complex_row(in, i)); + // set res row for (int j = 0; j < cols; j++){ for( int k = 0; k < 2; k++){ @@ -64,12 +60,8 @@ public static double[][][] fft(double[][][] in, boolean calcInv){ for(int j = 0; j < cols; j++){ // use fft on each col - double[][] res_col; - if(calcInv){ - res_col = ifft_one_dim(get_complex_col(res, j)); - } else { - res_col = fft_one_dim(get_complex_col(res, j)); - } + double[][] res_col = calcInv? ifft_one_dim(get_complex_col(res, j)) : fft_one_dim(get_complex_col(res, j)); + // set res col for (int i = 0; i < rows; i++){ for( int k = 0; k < 2; k++){ From d1deac8c7e51b7c5cc76316e12b7c6de2ea1f54c Mon Sep 17 00:00:00 2001 From: mufwan Date: Tue, 16 Jan 2024 18:50:32 +0100 Subject: [PATCH 029/133] stft --- .../runtime/matrix/data/LibMatrixSTFT.java | 108 ++++++++++++++++++ .../component/matrix/LibMatrixSTFTTest.java | 107 +++++++++++++++++ 2 files changed, 215 insertions(+) create mode 100644 src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixSTFT.java create mode 100644 src/test/java/org/apache/sysds/test/component/matrix/LibMatrixSTFTTest.java diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixSTFT.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixSTFT.java new file mode 100644 index 00000000000..4126194e314 --- /dev/null +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixSTFT.java @@ -0,0 +1,108 @@ +package org.apache.sysds.runtime.matrix.data; + +import java.util.Arrays; + +import static org.apache.sysds.runtime.matrix.data.LibMatrixFourier.fft; +import static org.apache.sysds.runtime.matrix.data.LibMatrixFourier.fft_one_dim; + + +public class LibMatrixSTFT { + + public static MatrixBlock[] stft(MatrixBlock re, MatrixBlock im, int windowSize, int overlap) { + /* + int dim = re.getNumRows(); + + double[][] in = new double[2][dim]; + in[0] = convertToArray(re); + //in[1] = convertToArray(im); + + double[][] res = one_dim_stft(in[0], windowSize, overlap); + + return convertToMatrixBlocks(res); + */ + int rows = re.getNumRows(); + int cols = re.getNumColumns(); + + double[][] in = new double[2][cols]; + in[0] = convertToArray(re); + in[1] = convertToArray(im); + + double[][] res = one_dim_stft(in[0], windowSize, overlap); + + return convertToMatrixBlocks(res); + } + + public static double[][] one_dim_stft(double[] signal, int windowSize, int overlap) { + if (windowSize < 1 || (windowSize & (windowSize - 1)) != 0) { + throw new IllegalArgumentException("frameSize is not a power of two"); + } + int stepSize = windowSize - overlap; + int totalFrames = (signal.length - overlap) / stepSize; + + // Initialize the STFT output array: [time frame][frequency bin][real & imaginary parts] + double[][] stftOutput = new double[2][totalFrames * windowSize]; + + for (int frame = 0; frame < totalFrames; frame++) { + double[] windowedSignal = new double[windowSize]; + + + // Apply window function (rectangular in this case) + for (int i = 0; i < windowSize; i++) { + int signalIndex = frame * stepSize + i; + windowedSignal[i] = (signalIndex < signal.length) ? signal[signalIndex] : 0; + } + + // Perform FFT on windowed signal + int l = windowedSignal.length; + double[][] tmp = new double[2][l]; + + for (int i = 0; i < l; i++) { + tmp[0][i] = windowedSignal[i]; + tmp[1][i] = 0; + } + double[][] fftResult = fft_one_dim(tmp); + + // Store the FFT result in the output array + int startIndex = windowSize * frame; + for (int i = 0; i < l; i++) { + stftOutput[0][startIndex + i] = fftResult[0][i]; + stftOutput[1][startIndex + i] = fftResult[1][i]; + } + //stftOutput[frame] = fftResult; + } + + return stftOutput; + } + + + private static MatrixBlock[] convertToMatrixBlocks(double[][] in){ + + + int cols = in[0].length; + + MatrixBlock re = new MatrixBlock(1, cols, in[0]); + MatrixBlock im = new MatrixBlock(1, cols, in[1]); + + return new MatrixBlock[]{re, im}; + } + + + private static double[] convertToArray(MatrixBlock in){ + return in.getDenseBlockValues(); + } + + public static MatrixBlock[] stft(double[] in, int windowSize, int overlap){ + double[][] arr = new double[2][in.length]; + arr[0] = in; + return stft(convertToMatrixBlocks(arr), windowSize, overlap); + } + + public static MatrixBlock[] stft(double[][] in, int windowSize, int overlap){ + return stft(convertToMatrixBlocks(in), windowSize, overlap); + } + + public static MatrixBlock[] stft(MatrixBlock[] in, int windowSize, int overlap){ + return stft(in[0], in[1], windowSize, overlap); + } + +} diff --git a/src/test/java/org/apache/sysds/test/component/matrix/LibMatrixSTFTTest.java b/src/test/java/org/apache/sysds/test/component/matrix/LibMatrixSTFTTest.java new file mode 100644 index 00000000000..c9850a1b90e --- /dev/null +++ b/src/test/java/org/apache/sysds/test/component/matrix/LibMatrixSTFTTest.java @@ -0,0 +1,107 @@ +package org.apache.sysds.test.component.matrix; + +import org.apache.sysds.runtime.matrix.data.MatrixBlock; +import org.junit.Test; +//import static org.apache.sysds.runtime.matrix.data.LibMatrixFourier.*; +import static org.junit.Assert.assertArrayEquals; +import static org.apache.sysds.runtime.matrix.data.LibMatrixSTFT.*; + + +public class LibMatrixSTFTTest { + + @Test + public void simple_test() { + + double[] signal = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}; + int windowSize = 4; + int overlap = 2; + double[][] stftResult = one_dim_stft(signal, windowSize, overlap); + + // 1st row real part, 2nd row imaginary part + double[][] expected = {{6, -2, -2, -2, 14, -2, -2, -2, 22, -2, -2, -2, 30, -2, -2, -2, 38, -2, -2, -2, 46, -2, -2, -2, 54, -2, -2, -2},{0, 2, 0, -2, 0, 2, 0, -2, 0, 2, 0, -2, 0, 2, 0, -2, 0, 2, 0, -2, 0, 2, 0, -2, 0, 2, 0, -2}}; + + for(double[] row : stftResult){ + for (double elem : row){ + System.out.print(elem + " "); + } + System.out.println(); + } + assertArrayEquals(expected[0], stftResult[0], 0.0001); + assertArrayEquals(expected[1], stftResult[1], 0.0001); + } + + @Test + public void matrix_block_one_dim_test(){ + + double[] in = {0, 18, -15, 3}; + + double[] expected_re = {6,15,-36,15}; + double[] expected_im = {0,-15,0,15}; + + MatrixBlock[] res = stft(in, 4, 0); + double[] res_re = res[0].getDenseBlockValues(); + double[] res_im = res[1].getDenseBlockValues(); + + for(double elem : res_re){ + System.out.print(elem+" "); + } + System.out.println(); + for(double elem : res_im){ + System.out.print(elem+" "); + } + + assertArrayEquals(expected_re, res_re, 0.0001); + assertArrayEquals(expected_im, res_im, 0.0001); + } + + @Test + public void matrix_block_one_dim_test2(){ + + double[] in = {10, 5, -3, 8, 15, -6, 2, 0}; + + double[] expected_re = {20.0, 13.0, -6.0, 13.0, 14.0, -18.0, 10.0, -18.0, 11.0, 13.0, 23.0, 13.0}; + double[] expected_im = {0.0, 3.0, 0.0, -3.0, 0.0, -14.0, 0.0, 14.0, 0.0, 6.0, 0.0, -6.0 }; + + MatrixBlock[] res = stft(in, 4, 2); + double[] res_re = res[0].getDenseBlockValues(); + double[] res_im = res[1].getDenseBlockValues(); + + for(double elem : res_re){ + System.out.print(elem+" "); + } + System.out.println(); + for(double elem : res_im){ + System.out.print(elem+" "); + } + + assertArrayEquals(expected_re, res_re, 0.0001); + assertArrayEquals(expected_im, res_im, 0.0001); + } + + /* + public static void main(String[] args) { + + + // Generate the sinusoidal signal + //double[] signal = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}; + double[] signal = {10, 5, -3, 8, 15, -6, 2, 0}; + + + // Define STFT parameters + int frameSize = 4; + int overlap = 2; + + // Perform the STFT + double[][] stftResult = one_dim_stft(signal, frameSize, overlap); + + // tensorflow change arguments names it is calles step + // Output some results for verification + // also for 2d array + System.out.println("STFT Result (a few samples):"); + int l = stftResult[0].length; + for (int i = 0; i < l; i++) { + System.out.println("Real = " + stftResult[0][i] + ", Imaginary = " + stftResult[1][i]); + } + } + */ +} \ No newline at end of file From 3bf7b6610fcb208603a62e776a557c58ce06d25b Mon Sep 17 00:00:00 2001 From: Jessica Priebe Date: Wed, 17 Jan 2024 15:01:52 +0100 Subject: [PATCH 030/133] fft and ifft with double[] array --- .../runtime/matrix/data/LibCommonsMath.java | 6 +- .../runtime/matrix/data/LibMatrixFourier.java | 222 +++------- .../matrix/data/LibMatrixFourierOld.java | 238 +++++++++++ .../runtime/matrix/data/LibMatrixSTFT.java | 6 +- .../test/component/matrix/FourierOldTest.java | 101 +++++ .../test/component/matrix/FourierTest.java | 190 +++++---- .../test/component/matrix/FourierTestData.py | 30 ++ .../matrix/FourierTestWithFiles.java | 398 ++++++++++-------- .../matrix/FourierTestWithFilesOld.java | 384 +++++++++++++++++ 9 files changed, 1148 insertions(+), 427 deletions(-) create mode 100644 src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourierOld.java create mode 100644 src/test/java/org/apache/sysds/test/component/matrix/FourierOldTest.java create mode 100644 src/test/java/org/apache/sysds/test/component/matrix/FourierTestWithFilesOld.java diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java index 55c9161f248..007a8feddd4 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java @@ -49,7 +49,7 @@ import org.apache.sysds.runtime.matrix.operators.BinaryOperator; import org.apache.sysds.runtime.util.DataConverter; -import static org.apache.sysds.runtime.matrix.data.LibMatrixFourier.*; +import static org.apache.sysds.runtime.matrix.data.LibMatrixFourierOld.*; /** * Library for matrix operations that need invocation of @@ -263,7 +263,7 @@ private static MatrixBlock[] computeFFT(MatrixBlock in) { throw new DMLRuntimeException("Invalid empty block"); //run fft - return fft(in); + return fft_old(in); } private static MatrixBlock[] computeIFFT(MatrixBlock in) { @@ -274,7 +274,7 @@ private static MatrixBlock[] computeIFFT(MatrixBlock in) { MatrixBlock inIm = new MatrixBlock(rows, cols, new double[cols*rows]); //run ifft - return ifft(in, inIm); + return ifft_old(in, inIm); } /** diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java index f622af3379e..61124a60b24 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java @@ -13,224 +13,116 @@ public static MatrixBlock[] fft(MatrixBlock re, MatrixBlock im){ int rows = re.getNumRows(); int cols = re.getNumColumns(); + if(!isPowerOfTwo(rows) || !isPowerOfTwo(cols)) throw new RuntimeException("false dimensions"); - double[][][] in = new double[2][rows][cols]; - in[0] = convertToArray(re); - in[1] = convertToArray(im); + fft(re.getDenseBlockValues(), im.getDenseBlockValues(), rows, cols); - double[][][] res = fft(in, false); - - return convertToMatrixBlocks(res); + return new MatrixBlock[]{re, im}; } public static MatrixBlock[] ifft(MatrixBlock re, MatrixBlock im){ int rows = re.getNumRows(); int cols = re.getNumColumns(); + if(!isPowerOfTwo(rows) || !isPowerOfTwo(cols)) throw new RuntimeException("false dimensions"); - double[][][] in = new double[2][rows][cols]; - in[0] = convertToArray(re); - in[1] = convertToArray(im); - - double[][][] res = fft(in, true); + ifft(re.getDenseBlockValues(), im.getDenseBlockValues(), rows, cols); - return convertToMatrixBlocks(res); + return new MatrixBlock[]{re, im}; } - public static double[][][] fft(double[][][] in, boolean calcInv){ - - int rows = in[0].length; - int cols = in[0][0].length; + public static void fft(double[] re, double[] im, int rows, int cols) { - double[][][] res = new double[2][rows][cols]; + double[] re_inter = new double[rows*cols]; + double[] im_inter = new double[rows*cols]; for(int i = 0; i < rows; i++){ - // use fft or ifft on each row - double[][] res_row = calcInv? ifft_one_dim(get_complex_row(in, i)) : fft_one_dim(get_complex_row(in, i)); - - // set res row - for (int j = 0; j < cols; j++){ - for( int k = 0; k < 2; k++){ - res[k][i][j] = res_row[k][j]; - } - } + fft_one_dim(re, im, re_inter, im_inter, i*cols, 1, cols, cols); } - if(rows == 1) return res; - for(int j = 0; j < cols; j++){ - // use fft on each col - double[][] res_col = calcInv? ifft_one_dim(get_complex_col(res, j)) : fft_one_dim(get_complex_col(res, j)); - - // set res col - for (int i = 0; i < rows; i++){ - for( int k = 0; k < 2; k++){ - res[k][i][j] = res_col[k][i]; - } - } + fft_one_dim(re, im, re_inter, im_inter, j, cols, rows, rows*cols); } - return res; } - public static double[][] fft_one_dim(double[][] in){ - // 1st row real part, 2nd row imaginary part - if(in == null || in.length != 2 || in[0].length != in[1].length) throw new RuntimeException("in false dimensions"); + public static void ifft(double[] re, double[] im, int rows, int cols) { - int cols = in[0].length; - if(cols == 1) return in; + double[] re_inter = new double[rows*cols]; + double[] im_inter = new double[rows*cols]; - double angle = -2*FastMath.PI/cols; - - // split values depending on index - double[][] even = new double[2][cols/2]; - double[][] odd = new double[2][cols/2]; - - for(int i = 0; i < 2; i++){ - for (int j = 0; j < cols/2; j++){ - even[i][j] = in[i][j*2]; - odd[i][j] = in[i][j*2+1]; - } + for(int j = 0; j < cols; j++){ + ifft_one_dim(re, im, re_inter, im_inter, j, cols, rows, rows*cols); } - double[][] res_even = fft_one_dim(even); - double[][] res_odd = fft_one_dim(odd); - - double[][] res = new double[2][cols]; - - for(int j=0; j < cols/2; j++){ - double[] omega_pow = new double[]{FastMath.cos(j*angle), FastMath.sin(j*angle)}; - - // m = omega * res_odd[j] - double[] m = new double[]{ - omega_pow[0] * res_odd[0][j] - omega_pow[1] * res_odd[1][j], - omega_pow[0] * res_odd[1][j] + omega_pow[1] * res_odd[0][j]}; - // res[j] = res_even + m; - // res[j+cols/2] = res_even - m; - for(int i = 0; i < 2; i++){ - res[i][j] = res_even[i][j] + m[i]; - res[i][j+cols/2] = res_even[i][j] - m[i]; - } + for(int i = 0; i < rows; i++){ + ifft_one_dim(re, im, re_inter, im_inter, i*cols, 1, cols, cols); } - - return res; - } - public static double[][] ifft_one_dim(double[][] in) { + public static void fft_one_dim(double[] re, double[] im, double[] re_inter, double[] im_inter, int start, int step, int num, int subArraySize) { - // cols[0] is real part, cols[1] is imaginary part - int cols = in[0].length; + if(num == 1) return; + double angle = -2*FastMath.PI/num; - // conjugate input - in[1] = Arrays.stream(in[1]).map(i -> -i).toArray(); - - // apply fft - double[][] res = fft_one_dim(in); + // fft for even indices + fft_one_dim(re, im, re_inter, im_inter, start, step*2, num/2, subArraySize); + // fft for odd indices + fft_one_dim(re, im, re_inter, im_inter,start+step, step*2, num/2, subArraySize); - // conjugate and scale result - res[0] = Arrays.stream(res[0]).map(i -> i/cols).toArray(); - res[1] = Arrays.stream(res[1]).map(i -> -i/cols).toArray(); + // iterates over even indices + for(int j = start, cnt = 0; cnt < num/2; j+=(2*step), cnt++){ - return res; - } + double omega_pow_re = FastMath.cos(cnt*angle); + double omega_pow_im = FastMath.sin(cnt*angle); - private static MatrixBlock[] convertToMatrixBlocks(double[][][] in){ + // calculate m using the result of odd index + double m_re = omega_pow_re * re[j+step] - omega_pow_im * im[j+step]; + double m_im = omega_pow_re * im[j+step] + omega_pow_im * re[j+step]; - int cols = in[0][0].length; - int rows = in[0].length; + int index = start+cnt*step; + re_inter[index] = re[j] + m_re; + re_inter[index+subArraySize/2] = re[j] - m_re; - double[] flattened_re = Arrays.stream(in[0]).flatMapToDouble(Arrays::stream).toArray(); - double[] flattened_im = new double[rows*cols]; - if(in.length > 1){ - flattened_im = Arrays.stream(in[1]).flatMapToDouble(Arrays::stream).toArray(); + im_inter[index] = im[j] + m_im; + im_inter[index+subArraySize/2] = im[j] - m_im; } - MatrixBlock re = new MatrixBlock(rows, cols, flattened_re); - MatrixBlock im = new MatrixBlock(rows, cols, flattened_im); - - return new MatrixBlock[]{re, im}; - } - - private static MatrixBlock getZeroMatrixBlock(int rows, int cols){ - - return new MatrixBlock(rows, cols, new double[cols*rows]); - - } - - private static double[][] convertToArray(MatrixBlock in){ - - int rows = in.getNumRows(); - int cols = in.getNumColumns(); - - double[][] out = new double[rows][cols]; - for(int i = 0; i < rows; i++){ - out[i] = Arrays.copyOfRange(in.getDenseBlockValues(), i * cols, (i+1) * cols); + for(int j = start; j < start+subArraySize; j+=step){ + re[j] = re_inter[j]; + im[j] = im_inter[j]; + re_inter[j] = 0; + im_inter[j] = 0; } - - return out; } - private static double[][][] convertToArray(MatrixBlock[] in){ - - int rows = in[0].getNumRows(); - int cols = in[0].getNumColumns(); - double[][][] out = new double[2][rows][cols]; - for(int k = 0; k < 2; k++){ - for(int i = 0; i < rows; i++){ - out[k][i] = Arrays.copyOfRange(in[k].getDenseBlockValues(), i * cols, (i+1) * cols); - } - } - - return out; - } + public static void ifft_one_dim(double[] re, double[] im, double[] re_inter, double[] im_inter, int start, int step, int num, int subArraySize) { - public static double[][] get_complex_row(double[][][] in, int i){ - - int cols = in[0][0].length; - - double[][] row = new double[2][cols]; - // get row - for (int j = 0; j < cols; j++){ - for( int k = 0; k < 2; k++){ - row[k][j] = in[k][i][j]; - } + // conjugate input + for (int i = start; i < start+num*step; i+=step){ + im[i] = -im[i]; } - return row; - } - public static double[][] get_complex_col(double[][][] in, int j){ - int rows = in[0].length; + // apply fft + fft_one_dim(re, im, re_inter, im_inter, start, step, num, subArraySize); - double[][] col = new double[2][rows]; - // get row - for (int i = 0; i < rows; i++){ - for( int k = 0; k < 2; k++){ - col[k][i] = in[k][i][j]; - } + // conjugate and scale result + for (int i = start; i < start+num*step; i+=step){ + re[i] = re[i]/num; + im[i] = -im[i]/num; } - return col; } private static boolean isPowerOfTwo(int n){ - return ((n != 0) && ((n & (n - 1)) == 0)) || n == 1; + return (n != 0) && ((n & (n - 1)) == 0); } - public static MatrixBlock[] fft(double[] in){ - double[][][] arr = new double[2][1][in.length]; - arr[0][0] = in; - return fft(convertToMatrixBlocks(arr)); - } - - public static MatrixBlock[] fft(double[][][] in){ - return fft(convertToMatrixBlocks(in)); + public static MatrixBlock[] fft(MatrixBlock re){ + return fft(re, new MatrixBlock(re.getNumRows(),re.getNumColumns(), new double[re.getNumRows()*re.getNumColumns()])); } - public static MatrixBlock[] fft(MatrixBlock[] in){ - return fft(in[0], in[1]); + public static MatrixBlock[] ifft(MatrixBlock re){ + return ifft(re, new MatrixBlock(re.getNumRows(),re.getNumColumns(), new double[re.getNumRows()*re.getNumColumns()])); } - public static MatrixBlock[] fft(MatrixBlock re){ - return fft(re, getZeroMatrixBlock(re.getNumRows(), re.getNumColumns())); - } } diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourierOld.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourierOld.java new file mode 100644 index 00000000000..832d6955fec --- /dev/null +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourierOld.java @@ -0,0 +1,238 @@ +package org.apache.sysds.runtime.matrix.data; + +import org.apache.commons.math3.util.FastMath; + +import java.util.Arrays; + +public class LibMatrixFourierOld { + + /** + * Function to perform Fast Fourier Transformation + */ + + public static MatrixBlock[] fft_old(MatrixBlock re, MatrixBlock im){ + + int rows = re.getNumRows(); + int cols = re.getNumColumns(); + + double[][][] in = new double[2][rows][cols]; + in[0] = convertToArray(re); + in[1] = convertToArray(im); + + double[][][] res = fft_old(in, false); + + return convertToMatrixBlocks(res); + } + + public static MatrixBlock[] ifft_old(MatrixBlock re, MatrixBlock im){ + + int rows = re.getNumRows(); + int cols = re.getNumColumns(); + + double[][][] in = new double[2][rows][cols]; + in[0] = convertToArray(re); + in[1] = convertToArray(im); + + double[][][] res = fft_old(in, true); + + return convertToMatrixBlocks(res); + } + + public static double[][][] fft_old(double[][][] in, boolean calcInv){ + + int rows = in[0].length; + int cols = in[0][0].length; + + double[][][] res = new double[2][rows][cols]; + + for(int i = 0; i < rows; i++){ + // use fft or ifft on each row + double[][] res_row = calcInv? ifft_one_dim_old(get_complex_row(in, i)) : fft_one_dim_old(get_complex_row(in, i)); + + // set res row + for (int j = 0; j < cols; j++){ + for( int k = 0; k < 2; k++){ + res[k][i][j] = res_row[k][j]; + } + } + } + + if(rows == 1) return res; + + for(int j = 0; j < cols; j++){ + // use fft on each col + double[][] res_col = calcInv? ifft_one_dim_old(get_complex_col(res, j)) : fft_one_dim_old(get_complex_col(res, j)); + + // set res col + for (int i = 0; i < rows; i++){ + for( int k = 0; k < 2; k++){ + res[k][i][j] = res_col[k][i]; + } + } + } + + return res; + } + + public static double[][] fft_one_dim_old(double[][] in){ + // 1st row real part, 2nd row imaginary part + if(in == null || in.length != 2 || in[0].length != in[1].length) throw new RuntimeException("in false dimensions"); + + int cols = in[0].length; + if(cols == 1) return in; + + double angle = -2* FastMath.PI/cols; + + // split values depending on index + double[][] even = new double[2][cols/2]; + double[][] odd = new double[2][cols/2]; + + for(int i = 0; i < 2; i++){ + for (int j = 0; j < cols/2; j++){ + even[i][j] = in[i][j*2]; + odd[i][j] = in[i][j*2+1]; + } + } + + double[][] res_even = fft_one_dim_old(even); + double[][] res_odd = fft_one_dim_old(odd); + + double[][] res = new double[2][cols]; + + for(int j=0; j < cols/2; j++){ + double[] omega_pow = new double[]{FastMath.cos(j*angle), FastMath.sin(j*angle)}; + + // m = omega * res_odd[j] + double[] m = new double[]{ + omega_pow[0] * res_odd[0][j] - omega_pow[1] * res_odd[1][j], + omega_pow[0] * res_odd[1][j] + omega_pow[1] * res_odd[0][j]}; + + // res[j] = res_even + m; + // res[j+cols/2] = res_even - m; + for(int i = 0; i < 2; i++){ + res[i][j] = res_even[i][j] + m[i]; + res[i][j+cols/2] = res_even[i][j] - m[i]; + } + } + + return res; + + } + + public static double[][] ifft_one_dim_old(double[][] in) { + + // cols[0] is real part, cols[1] is imaginary part + int cols = in[0].length; + + // conjugate input + in[1] = Arrays.stream(in[1]).map(i -> -i).toArray(); + + // apply fft + double[][] res = fft_one_dim_old(in); + + // conjugate and scale result + res[0] = Arrays.stream(res[0]).map(i -> i/cols).toArray(); + res[1] = Arrays.stream(res[1]).map(i -> -i/cols).toArray(); + + return res; + } + + private static MatrixBlock[] convertToMatrixBlocks(double[][][] in){ + + int cols = in[0][0].length; + int rows = in[0].length; + + double[] flattened_re = Arrays.stream(in[0]).flatMapToDouble(Arrays::stream).toArray(); + double[] flattened_im = new double[rows*cols]; + if(in.length > 1){ + flattened_im = Arrays.stream(in[1]).flatMapToDouble(Arrays::stream).toArray(); + } + + MatrixBlock re = new MatrixBlock(rows, cols, flattened_re); + MatrixBlock im = new MatrixBlock(rows, cols, flattened_im); + + return new MatrixBlock[]{re, im}; + } + + private static MatrixBlock getZeroMatrixBlock(int rows, int cols){ + + return new MatrixBlock(rows, cols, new double[cols*rows]); + + } + + private static double[][] convertToArray(MatrixBlock in){ + + int rows = in.getNumRows(); + int cols = in.getNumColumns(); + + double[][] out = new double[rows][cols]; + for(int i = 0; i < rows; i++){ + out[i] = Arrays.copyOfRange(in.getDenseBlockValues(), i * cols, (i+1) * cols); + } + + return out; + } + private static double[][][] convertToArray(MatrixBlock[] in){ + + int rows = in[0].getNumRows(); + int cols = in[0].getNumColumns(); + + double[][][] out = new double[2][rows][cols]; + for(int k = 0; k < 2; k++){ + for(int i = 0; i < rows; i++){ + out[k][i] = Arrays.copyOfRange(in[k].getDenseBlockValues(), i * cols, (i+1) * cols); + } + } + + return out; + } + + public static double[][] get_complex_row(double[][][] in, int i){ + + int cols = in[0][0].length; + + double[][] row = new double[2][cols]; + // get row + for (int j = 0; j < cols; j++){ + for( int k = 0; k < 2; k++){ + row[k][j] = in[k][i][j]; + } + } + return row; + } + public static double[][] get_complex_col(double[][][] in, int j){ + + int rows = in[0].length; + + double[][] col = new double[2][rows]; + // get row + for (int i = 0; i < rows; i++){ + for( int k = 0; k < 2; k++){ + col[k][i] = in[k][i][j]; + } + } + return col; + } + + public static boolean isPowerOfTwo(int n){ + return (n != 0) && ((n & (n - 1)) == 0); + } + + public static MatrixBlock[] fft_old(double[] in){ + double[][][] arr = new double[2][1][in.length]; + arr[0][0] = in; + return fft_old(convertToMatrixBlocks(arr)); + } + + public static MatrixBlock[] fft_old(double[][][] in){ + return fft_old(convertToMatrixBlocks(in)); + } + + public static MatrixBlock[] fft_old(MatrixBlock[] in){ + return fft_old(in[0], in[1]); + } + + public static MatrixBlock[] fft_old(MatrixBlock re){ + return fft_old(re, getZeroMatrixBlock(re.getNumRows(), re.getNumColumns())); + } +} diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixSTFT.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixSTFT.java index 4126194e314..a50dcfe85ef 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixSTFT.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixSTFT.java @@ -2,8 +2,8 @@ import java.util.Arrays; -import static org.apache.sysds.runtime.matrix.data.LibMatrixFourier.fft; -import static org.apache.sysds.runtime.matrix.data.LibMatrixFourier.fft_one_dim; +import static org.apache.sysds.runtime.matrix.data.LibMatrixFourierOld.fft_old; +import static org.apache.sysds.runtime.matrix.data.LibMatrixFourierOld.fft_one_dim_old; public class LibMatrixSTFT { @@ -60,7 +60,7 @@ public static double[][] one_dim_stft(double[] signal, int windowSize, int overl tmp[0][i] = windowedSignal[i]; tmp[1][i] = 0; } - double[][] fftResult = fft_one_dim(tmp); + double[][] fftResult = fft_one_dim_old(tmp); // Store the FFT result in the output array int startIndex = windowSize * frame; diff --git a/src/test/java/org/apache/sysds/test/component/matrix/FourierOldTest.java b/src/test/java/org/apache/sysds/test/component/matrix/FourierOldTest.java new file mode 100644 index 00000000000..3168a00e5f9 --- /dev/null +++ b/src/test/java/org/apache/sysds/test/component/matrix/FourierOldTest.java @@ -0,0 +1,101 @@ +package org.apache.sysds.test.component.matrix; + +import org.apache.sysds.runtime.matrix.data.MatrixBlock; +import org.junit.Test; + +import static org.apache.sysds.runtime.matrix.data.LibMatrixFourierOld.fft_one_dim_old; +import static org.apache.sysds.runtime.matrix.data.LibMatrixFourierOld.ifft_one_dim_old; +import static org.apache.sysds.runtime.matrix.data.LibMatrixFourierOld.fft_old; +import static org.apache.sysds.runtime.matrix.data.LibMatrixFourierOld.ifft_old; +import static org.junit.Assert.assertArrayEquals; + +public class FourierOldTest { + + @Test + public void simple_test_one_dim() { + // 1st row real part, 2nd row imaginary part + double[][] in = {{0, 18, -15, 3},{0, 0, 0, 0}}; + double[][] expected = {{6, 15, -36, 15},{0, -15, 0, 15}}; + + double[][] res = fft_one_dim_old(in); + + assertArrayEquals(expected[0], res[0], 0.0001); + assertArrayEquals(expected[1], res[1], 0.0001); + } + + @Test + public void simple_test_two_dim() { + // tested with numpy + double[][][] in = {{{0, 18},{-15, 3}},{{0, 0},{0, 0}}}; + + double[][][] expected = {{{6, -36},{30, 0}},{{0, 0},{0, 0}}}; + + double[][][] res = fft_old(in, false); + + for(int k = 0; k < 2 ; k++){ + for(int i = 0; i < res[0].length; i++) { + assertArrayEquals(expected[k][i], res[k][i], 0.0001); + } + } + } + + @Test + public void simple_test_one_dim_ifft() { + + double[][] in = {{1, -2, 3, -4},{0, 0, 0, 0}}; + + double[][] res_fft = fft_one_dim_old(in); + double[][] res = ifft_one_dim_old(res_fft); + + assertArrayEquals(in[0], res[0], 0.0001); + assertArrayEquals(in[1], res[1], 0.0001); + } + + @Test + public void matrix_block_one_dim_test(){ + + double[] in = {0, 18, -15, 3}; + + double[] expected_re = {6,15,-36,15}; + double[] expected_im = {0,-15,0,15}; + + MatrixBlock[] res = fft_old(in); + double[] res_re = res[0].getDenseBlockValues(); + double[] res_im = res[1].getDenseBlockValues(); + + assertArrayEquals(expected_re, res_re, 0.0001); + assertArrayEquals(expected_im, res_im, 0.0001); + } + @Test + public void matrix_block_two_dim_test(){ + + double[][][] in = {{{0, 18},{-15, 3}}}; + + double[] flattened_expected_re = {6,-36, 30,0}; + double[] flattened_expected_im = {0,0,0,0}; + + MatrixBlock[] res = fft_old(in); + double[] res_re = res[0].getDenseBlockValues(); + double[] res_im = res[1].getDenseBlockValues(); + + assertArrayEquals(flattened_expected_re, res_re, 0.0001); + assertArrayEquals(flattened_expected_im, res_im, 0.0001); + } + + @Test + public void test_ifft_two_dim_matrixBlock() { + + MatrixBlock re = new MatrixBlock(2, 2, new double[]{6,-36, 30, 0}); + MatrixBlock im = new MatrixBlock(2, 2, new double[]{0, 0, 0, 0}); + + double[] expected_re = {0, 18, -15, 3}; + double[] expected_im = {0, 0, 0, 0}; + + MatrixBlock[] res = ifft_old(re, im); + + assertArrayEquals(expected_re, res[0].getDenseBlockValues(), 0.0001); + assertArrayEquals(expected_im, res[1].getDenseBlockValues(), 0.0001); + + } + +} diff --git a/src/test/java/org/apache/sysds/test/component/matrix/FourierTest.java b/src/test/java/org/apache/sysds/test/component/matrix/FourierTest.java index 3c52055b458..8e894af2601 100644 --- a/src/test/java/org/apache/sysds/test/component/matrix/FourierTest.java +++ b/src/test/java/org/apache/sysds/test/component/matrix/FourierTest.java @@ -2,111 +2,149 @@ import org.apache.sysds.runtime.matrix.data.MatrixBlock; import org.junit.Test; -import static org.apache.sysds.runtime.matrix.data.LibMatrixFourier.*; + +import static org.apache.sysds.runtime.matrix.data.LibMatrixFourier.fft_one_dim; +import static org.apache.sysds.runtime.matrix.data.LibMatrixFourier.fft; +import static org.apache.sysds.runtime.matrix.data.LibMatrixFourier.ifft; + import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertTrue; public class FourierTest { @Test - public void simple_test_one_dim() { - // 1st row real part, 2nd row imaginary part - double[][] in = {{0, 18, -15, 3},{0, 0, 0, 0}}; - double[][] expected = {{6, 15, -36, 15},{0, -15, 0, 15}}; - - double[][] res = fft_one_dim(in); - for(double[] row : res){ - for (double elem : row){ - System.out.print(elem + " "); - } - System.out.println(); - } - assertArrayEquals(expected[0], res[0], 0.0001); - assertArrayEquals(expected[1], res[1], 0.0001); + public void test_fft_one_dim() { + + double[] re = {0, 18, -15, 3}; + double[] im = {0, 0, 0, 0}; + + double[] re_inter = new double[4]; + double[] im_inter = new double[4]; + + double[] expected_re = {6, 15, -36, 15}; + double[] expected_im = {0, -15, 0, 15}; + + int cols = 4; + + fft_one_dim(re, im, re_inter, im_inter, 0, 1, cols, cols); + + assertArrayEquals(expected_re, re, 0.0001); + assertArrayEquals(expected_im, im, 0.0001); } @Test - public void simple_test_two_dim() { - // tested with numpy - double[][][] in = {{{0, 18},{-15, 3}},{{0, 0},{0, 0}}}; - - double[][][] expected = {{{6, -36},{30, 0}},{{0, 0},{0, 0}}}; - - double[][][] res = fft(in, false); - - for(double[][] matrix : res){ - for(double[] row : matrix) { - for (double elem : row) { - System.out.print(elem + " "); - } - System.out.println(); - } - System.out.println(); - } - - for(int k = 0; k < 2 ; k++){ - for(int i = 0; i < res[0].length; i++) { - assertArrayEquals(expected[k][i], res[k][i], 0.0001); - } - } + public void test_fft_one_dim_2() { + + double[] re = {0, 18, -15, 3, 5, 10, 5, 9}; + double[] im = new double[8]; + + double[] re_inter = new double[8]; + double[] im_inter = new double[8]; + + double[] expected_re = {35, 4.89949, 15, -14.89949, -45, -14.89949, 15, 4.89949}; + double[] expected_im = {0, 18.58579, -16, -21.41421, 0, 21.41421, 16, -18.58579}; + + int cols = 8; + + fft_one_dim(re, im, re_inter, im_inter, 0, 1, cols, cols); + + assertArrayEquals(expected_re, re, 0.0001); + assertArrayEquals(expected_im, im, 0.0001); } @Test - public void simple_test_one_dim_ifft() { + public void test_fft_one_dim_matrixBlock() { + + MatrixBlock re = new MatrixBlock(1, 4, new double[]{0, 18, -15, 3}); + MatrixBlock im = new MatrixBlock(1, 4, new double[]{0, 0, 0, 0}); + + double[] expected_re = {6, 15, -36, 15}; + double[] expected_im = {0, -15, 0, 15}; + + MatrixBlock[] res = fft(re, im); + + assertArrayEquals(expected_re, res[0].getDenseBlockValues(), 0.0001); + assertArrayEquals(expected_im, res[1].getDenseBlockValues(), 0.0001); + + } + + @Test + public void test_ifft_one_dim_matrixBlock_2() { + + double[] in_re = new double[]{1, -2, 3, -4}; + double[] in_im = new double[]{0, 0, 0, 0}; + + MatrixBlock re = new MatrixBlock(1, 4, in_re); + MatrixBlock im = new MatrixBlock(1, 4, in_im); + + MatrixBlock[] inter = fft(re, im); + MatrixBlock[] res = ifft(inter[0], inter[1]); + + assertArrayEquals(in_re, res[0].getDenseBlockValues(), 0.0001); + assertArrayEquals(in_im, res[1].getDenseBlockValues(), 0.0001); + } + + @Test + public void test_fft_two_dim_matrixBlock() { + + MatrixBlock re = new MatrixBlock(2, 2, new double[]{0, 18, -15, 3}); + MatrixBlock im = new MatrixBlock(2, 2, new double[]{0, 0, 0, 0}); + + double[] expected_re = {6,-36, 30, 0}; + double[] expected_im = {0, 0, 0, 0}; + + MatrixBlock[] res = fft(re, im); + + assertArrayEquals(expected_re, res[0].getDenseBlockValues(), 0.0001); + assertArrayEquals(expected_im, res[1].getDenseBlockValues(), 0.0001); + + } + + @Test + public void test_ifft_two_dim_matrixBlock() { + + MatrixBlock re = new MatrixBlock(2, 2, new double[]{6,-36, 30, 0}); + MatrixBlock im = new MatrixBlock(2, 2, new double[]{0, 0, 0, 0}); + + double[] expected_re = {0, 18, -15, 3}; + double[] expected_im = {0, 0, 0, 0}; - double[][] in = {{1, -2, 3, -4},{0, 0, 0, 0}}; + MatrixBlock[] res = ifft(re, im); - double[][] res_fft = fft_one_dim(in); - double[][] res = ifft_one_dim(res_fft); + assertArrayEquals(expected_re, res[0].getDenseBlockValues(), 0.0001); + assertArrayEquals(expected_im, res[1].getDenseBlockValues(), 0.0001); - assertArrayEquals(in[0], res[0], 0.0001); - assertArrayEquals(in[1], res[1], 0.0001); } @Test - public void matrix_block_one_dim_test(){ + public void test_fft_two_dim_matrixBlock_row_1() { - double[] in = {0, 18, -15, 3}; + MatrixBlock re = new MatrixBlock(1, 2, new double[]{0, 18}); + MatrixBlock im = new MatrixBlock(1, 2, new double[]{0, 0}); - double[] expected_re = {6,15,-36,15}; - double[] expected_im = {0,-15,0,15}; + double[] expected_re = {18, -18}; + double[] expected_im = {0, 0}; - MatrixBlock[] res = fft(in); - double[] res_re = res[0].getDenseBlockValues(); - double[] res_im = res[1].getDenseBlockValues(); + MatrixBlock[] res = fft(re, im); - for(double elem : res_re){ - System.out.print(elem+" "); - } - System.out.println(); - for(double elem : res_im){ - System.out.print(elem+" "); - } + assertArrayEquals(expected_re, res[0].getDenseBlockValues(), 0.0001); + assertArrayEquals(expected_im, res[1].getDenseBlockValues(), 0.0001); - assertArrayEquals(expected_re, res_re, 0.0001); - assertArrayEquals(expected_im, res_im, 0.0001); } @Test - public void matrix_block_two_dim_test(){ + public void test_fft_two_dim_matrixBlock_row_2() { - double[][][] in = {{{0, 18},{-15, 3}}}; + MatrixBlock re = new MatrixBlock(1, 2, new double[]{ -15, 3}); + MatrixBlock im = new MatrixBlock(1, 2, new double[]{0, 0}); - double[] flattened_expected_re = {6,-36, 30,0}; - double[] flattened_expected_im = {0,0,0,0}; + double[] expected_re = {-12, -18}; + double[] expected_im = {0, 0}; - MatrixBlock[] res = fft(in); - double[] res_re = res[0].getDenseBlockValues(); - double[] res_im = res[1].getDenseBlockValues(); + MatrixBlock[] res = fft(re, im); - for(double elem : res_re){ - System.out.print(elem+" "); - } - System.out.println(); - for(double elem : res_im){ - System.out.print(elem+" "); - } + assertArrayEquals(expected_re, res[0].getDenseBlockValues(), 0.0001); + assertArrayEquals(expected_im, res[1].getDenseBlockValues(), 0.0001); - assertArrayEquals(flattened_expected_re, res_re, 0.0001); - assertArrayEquals(flattened_expected_im, res_im, 0.0001); } } \ No newline at end of file diff --git a/src/test/java/org/apache/sysds/test/component/matrix/FourierTestData.py b/src/test/java/org/apache/sysds/test/component/matrix/FourierTestData.py index 23b38a44fdf..9b7aaa0233f 100644 --- a/src/test/java/org/apache/sysds/test/component/matrix/FourierTestData.py +++ b/src/test/java/org/apache/sysds/test/component/matrix/FourierTestData.py @@ -81,6 +81,25 @@ def generate_complex_inputs_2d(num_inputs, max_power): complex_array = real_part + 1j * imag_part yield complex_array +def compute_fft_2d(inputs): + + total_time = 0 + num_calculations = 0 + + for input_array in inputs: + start_time = time.time() + result = np.fft.fft2(input_array) + end_time = time.time() + + total_time += end_time - start_time + num_calculations += 1 + + if num_calculations % 1000 == 0: + average_time = total_time / num_calculations + print(f"Average execution time after {num_calculations} calculations: {average_time:.6f} seconds") + + yield result + def compute_ifft_2d(inputs): total_time = 0 @@ -152,6 +171,17 @@ def save_to_file_complex_2d(inputs, outputs, filename, mode='a'): batch_size = 1000 max_power = 3 +# Process and save 2D FFT data in batches +filename_2d = "complex_fft_2d_data.csv" +for i in range(0, num_inputs, batch_size): + current_batch = min(batch_size, num_inputs - i) + inputs_2d = list(generate_complex_inputs_2d(current_batch, max_power)) + outputs_2d = list(compute_fft_2d(inputs_2d)) + save_to_file_complex_2d(inputs_2d, outputs_2d, filename_2d, mode='a' if i > 0 else 'w') + print(f"Batch {i//batch_size + 1} out of {num_inputs//batch_size} saved to {filename_2d}") + +print("All complex 2D fft data processed and saved.") + # Process and save 2D IFFT data in batches filename_2d = "complex_ifft_2d_data.csv" for i in range(0, num_inputs, batch_size): diff --git a/src/test/java/org/apache/sysds/test/component/matrix/FourierTestWithFiles.java b/src/test/java/org/apache/sysds/test/component/matrix/FourierTestWithFiles.java index 6daed442df0..ddc2b66ea83 100644 --- a/src/test/java/org/apache/sysds/test/component/matrix/FourierTestWithFiles.java +++ b/src/test/java/org/apache/sysds/test/component/matrix/FourierTestWithFiles.java @@ -3,11 +3,11 @@ import org.apache.sysds.runtime.matrix.data.MatrixBlock; import org.junit.Test; -import java.io.BufferedReader; -import java.io.FileReader; -import java.io.IOException; +import java.io.*; + +import static org.apache.sysds.runtime.matrix.data.LibMatrixFourier.fft; +import static org.apache.sysds.runtime.matrix.data.LibMatrixFourier.ifft; -import static org.apache.sysds.runtime.matrix.data.LibMatrixFourier.*; import static org.junit.Assert.assertTrue; public class FourierTestWithFiles { @@ -18,7 +18,9 @@ public class FourierTestWithFiles { @Test public void testFftWithNumpyData() throws IOException { String filename = "fft_data.csv"; // Path to your CSV file - BufferedReader reader = new BufferedReader(new FileReader(filename)); + String path = "./src/test/java/org/apache/sysds/test/component/matrix/"; + + BufferedReader reader = new BufferedReader(new FileReader(path+filename)); String line; int lineNumber = 0; long totalTime = 0; // Total time for all FFT computations @@ -29,17 +31,20 @@ public void testFftWithNumpyData() throws IOException { String[] values = line.split(","); int n = values.length / 3; - double[][][] input = new double[2][1][n]; + + double[] re = new double[n]; + double[] im = new double[n]; + double[][] expected = new double[2][n]; // First row for real, second row for imaginary parts for (int i = 0; i < n; i++) { - input[0][0][i] = Double.parseDouble(values[i]); + re[i] = Double.parseDouble(values[i]); expected[0][i] = Double.parseDouble(values[n + i]); // Real part expected[1][i] = Double.parseDouble(values[n * 2 + i]); // Imaginary part } long startTime = System.nanoTime(); - MatrixBlock[] actualBlocks = fft(input); + fft(re, im, 1, n); long endTime = System.nanoTime(); if(lineNumber > 1000){ @@ -52,19 +57,21 @@ public void testFftWithNumpyData() throws IOException { } } + double[][] actual = {re, im}; + // Validate the FFT results - validateFftResults(expected, actualBlocks, lineNumber); + validateFftResults(expected, actual, lineNumber); } reader.close(); } - private void validateFftResults(double[][] expected, MatrixBlock[] actualBlocks, int lineNumber) { + private void validateFftResults(double[][] expected, double[][] actual, int lineNumber) { int length = expected[0].length; for (int i = 0; i < length; i++) { - double realActual = actualBlocks[0].getValueDenseUnsafe(0, i); - double imagActual = actualBlocks[1].getValueDenseUnsafe(0, i); + double realActual = actual[0][i]; + double imagActual = actual[1][i]; assertEquals("Mismatch in real part at index " + i + " in line " + lineNumber, expected[0][i], realActual, 1e-9); assertEquals("Mismatch in imaginary part at index " + i + " in line " + lineNumber, expected[1][i], imagActual, 1e-9); } @@ -77,7 +84,9 @@ private void validateFftResults(double[][] expected, MatrixBlock[] actualBlocks, @Test public void testFftExecutionTime() throws IOException { String filename = "fft_data.csv"; // Path to your CSV file - BufferedReader reader = new BufferedReader(new FileReader(filename)); + String path = "./src/test/java/org/apache/sysds/test/component/matrix/"; + + BufferedReader reader = new BufferedReader(new FileReader(path+filename)); String line; int lineNumber = 0; long totalTime = 0; // Total time for all FFT computations @@ -87,15 +96,19 @@ public void testFftExecutionTime() throws IOException { lineNumber++; String[] values = line.split(","); int n = values.length / 3; - double[][][] input = new double[2][1][n]; + + double[] re = new double[n]; + double[] im = new double[n]; for (int i = 0; i < n; i++) { - input[0][0][i] = Double.parseDouble(values[i]); // Real part - input[1][0][i] = Double.parseDouble(values[n + i]); // Imaginary part + re[i] = Double.parseDouble(values[i]); // Real part + im[i] = Double.parseDouble(values[n + i]); // Imaginary part } long startTime = System.nanoTime(); - fft(input, false); + + fft(re, im, 1, n); + long endTime = System.nanoTime(); if(lineNumber > 1000){ totalTime += (endTime - startTime); @@ -103,7 +116,7 @@ public void testFftExecutionTime() throws IOException { if (numCalculations % progressInterval == 0) { double averageTime = (totalTime / 1e6) / numCalculations; // Average time in milliseconds - System.out.println("fft(double[][][] in, boolean calcInv) Average execution time after " + numCalculations + " calculations: " + String.format("%.8f", averageTime/1000) + " s"); + System.out.println("fft_old(double[][][] in, boolean calcInv) Average execution time after " + numCalculations + " calculations: " + String.format("%.8f", averageTime/1000) + " s"); } } } @@ -114,7 +127,9 @@ public void testFftExecutionTime() throws IOException { @Test public void testFftExecutionTimeOfOneDimFFT() throws IOException { String filename = "fft_data.csv"; // Path to your CSV file - BufferedReader reader = new BufferedReader(new FileReader(filename)); + String path = "./src/test/java/org/apache/sysds/test/component/matrix/"; + + BufferedReader reader = new BufferedReader(new FileReader(path+filename)); String line; int lineNumber = 0; long totalTime = 0; // Total time for all FFT computations @@ -124,15 +139,19 @@ public void testFftExecutionTimeOfOneDimFFT() throws IOException { lineNumber++; String[] values = line.split(","); int n = values.length / 2; - double[][] input = new double[2][n]; // First row for real, second row for imaginary parts + + double[] re = new double[n]; + double[] im = new double[n]; for (int i = 0; i < n; i++) { - input[0][i] = Double.parseDouble(values[i]); // Real part - input[1][i] = Double.parseDouble(values[n + i]); // Imaginary part + re[i] = Double.parseDouble(values[i]); // Real part + im[i] = Double.parseDouble(values[n + i]); // Imaginary part } long startTime = System.nanoTime(); - fft_one_dim(input); + // one dimensional + fft(re, im, 1, n); + long endTime = System.nanoTime(); if(lineNumber > 1000){ totalTime += (endTime - startTime); @@ -153,7 +172,9 @@ public void testFftExecutionTimeOfOneDimFFT() throws IOException { @Test public void testIfftWithRealNumpyData() throws IOException { String filename = "ifft_data.csv"; // Path to your CSV file - BufferedReader reader = new BufferedReader(new FileReader(filename)); + String path = "./src/test/java/org/apache/sysds/test/component/matrix/"; + + BufferedReader reader = new BufferedReader(new FileReader(path+filename)); String line; int lineNumber = 0; @@ -161,204 +182,221 @@ public void testIfftWithRealNumpyData() throws IOException { lineNumber++; String[] values = line.split(","); int n = values.length / 3; - double[][][] input = new double[2][1][n]; + + double[] re = new double[n]; + double[] im = new double[n]; + double[][] expected = new double[2][n]; // First row for real, second row for imaginary parts for (int i = 0; i < n; i++) { - input[0][0][i] = Double.parseDouble(values[i]); // Real part of input + re[i] = Double.parseDouble(values[i]); // Real part of input // Imaginary part of input is assumed to be 0 expected[0][i] = Double.parseDouble(values[n + i]); // Real part of expected output expected[1][i] = Double.parseDouble(values[n * 2 + i]); // Imaginary part of expected output } - - double[][][] actualResult = fft(input, true); // Perform IFFT - + + ifft(re, im, 1, n); // Perform IFFT + + double[][] actual = new double[][]{re, im}; // Validate the IFFT results - validateFftResults(expected, actualResult, lineNumber); + validateFftResults(expected, actual, lineNumber); } reader.close(); } - - private void validateFftResults(double[][] expected, double[][][] actualResult, int lineNumber) { + + + @Test + public void testIfftWithComplexNumpyData() throws IOException { + String filename = "complex_ifft_data.csv"; // Adjusted path to your IFFT data file with complex inputs + String path = "./src/test/java/org/apache/sysds/test/component/matrix/"; + + BufferedReader reader = new BufferedReader(new FileReader(path+filename)); + String line; + int lineNumber = 0; + long totalTime = 0; // Total time for all IFFT computations + int numCalculations = 0; // Number of IFFT computations + + while ((line = reader.readLine()) != null) { + lineNumber++; + String[] values = line.split(","); + int n = values.length / 4; // Adjusted for complex numbers + + // Real and imaginary parts + double[] re = new double[n]; + double[] im = new double[n]; + + double[][] expected = new double[2][n]; // Expected real and imaginary parts + + for (int i = 0; i < n; i++) { + re[i] = Double.parseDouble(values[i]); // Real part of input + im[i] = Double.parseDouble(values[i + n]); // Imaginary part of input + expected[0][i] = Double.parseDouble(values[i + 2 * n]); // Expected real part + expected[1][i] = Double.parseDouble(values[i + 3 * n]); // Expected imaginary part + } + + long startTime = System.nanoTime(); + + ifft(re, im, 1, n); // Perform IFFT + + long endTime = System.nanoTime(); + + if (lineNumber > 1000) { + totalTime += (endTime - startTime); + numCalculations++; + } + + double[][] actual = new double[][]{re, im}; + // Validate the IFFT results + validateComplexIFftResults(expected, actual, lineNumber); + + if (lineNumber % progressInterval == 0) { + double averageTime = (totalTime / 1e6) / numCalculations; // Average time in milliseconds + System.out.println("ifft: Average execution time after " + numCalculations + " calculations: " + String.format("%.8f", averageTime / 1000) + " s"); + } + } + + reader.close(); + } + + private void validateComplexIFftResults(double[][] expected, double[][] actual, int lineNumber) { int length = expected[0].length; for (int i = 0; i < length; i++) { - double realActual = actualResult[0][0][i]; - double imagActual = actualResult[1][0][i]; + double realActual = actual[0][i]; + double imagActual = actual[1][i]; assertEquals("Mismatch in real part at index " + i + " in line " + lineNumber, expected[0][i], realActual, 1e-9); assertEquals("Mismatch in imaginary part at index " + i + " in line " + lineNumber, expected[1][i], imagActual, 1e-9); } - if(lineNumber % progressInterval == 0){ - System.out.println("ifft(real input): Finished processing line " + lineNumber); + if (lineNumber % progressInterval == 0) { + System.out.println("ifft(complex input): Finished processing line " + lineNumber); } - } - @Test -public void testIfftWithComplexNumpyData() throws IOException { - String filename = "complex_ifft_data.csv"; // Adjusted path to your IFFT data file with complex inputs - BufferedReader reader = new BufferedReader(new FileReader(filename)); - String line; - int lineNumber = 0; - long totalTime = 0; // Total time for all IFFT computations - int numCalculations = 0; // Number of IFFT computations - - while ((line = reader.readLine()) != null) { - lineNumber++; - String[] values = line.split(","); - int n = values.length / 4; // Adjusted for complex numbers - double[][][] input = new double[2][1][n]; // Real and imaginary parts - double[][] expected = new double[2][n]; // Expected real and imaginary parts - - for (int i = 0; i < n; i++) { - input[0][0][i] = Double.parseDouble(values[i]); // Real part of input - input[1][0][i] = Double.parseDouble(values[i + n]); // Imaginary part of input - expected[0][i] = Double.parseDouble(values[i + 2 * n]); // Expected real part - expected[1][i] = Double.parseDouble(values[i + 3 * n]); // Expected imaginary part - } + public void testFft2dWithNumpyData() throws IOException { + String filename = "complex_fft_2d_data.csv"; // path to your 2D FFT data file + String path = "./src/test/java/org/apache/sysds/test/component/matrix/"; + + BufferedReader reader = new BufferedReader(new FileReader(path+filename)); + String line; + int lineNumber = 0; + long totalTime = 0; // Total time for all FFT 2D computations + int numCalculations = 0; // Number of FFT 2D computations + + while ((line = reader.readLine()) != null) { + lineNumber++; + String[] values = line.split(","); + int halfLength = values.length / 4; + int sideLength = (int) Math.sqrt(halfLength); // Assuming square matrix - long startTime = System.nanoTime(); - double[][][] actualResult = fft(input, true); // Perform IFFT - long endTime = System.nanoTime(); + // Real and imaginary parts + double[] re = new double[sideLength*sideLength]; + double[] im = new double[sideLength*sideLength]; - if (lineNumber > 1000) { + double[][][] expected = new double[2][sideLength][sideLength]; + + for (int i = 0; i < halfLength; i++) { + int row = i / sideLength; + int col = i % sideLength; + + // i == row*sideLength+col?! + re[row*sideLength+col] = Double.parseDouble(values[i]); + im[row*sideLength+col] = Double.parseDouble(values[i + halfLength]); + expected[0][row][col] = Double.parseDouble(values[i + 2 * halfLength]); + expected[1][row][col] = Double.parseDouble(values[i + 3 * halfLength]); + } + + long startTime = System.nanoTime(); + // Use your fft2d implementation + fft(re, im, sideLength, sideLength); + //double[][][] javaFftResult = fft_old(input, false); // Use your fft2d implementation + long endTime = System.nanoTime(); totalTime += (endTime - startTime); numCalculations++; - } - // Validate the IFFT results - validateComplexIFftResults(expected, actualResult, lineNumber); + for (int i = 0; i < sideLength; i++) { + for (int j = 0; j < sideLength; j++) { + assertComplexEquals("Mismatch at [" + i + "][" + j + "] in line " + lineNumber, + expected[0][i][j], expected[1][i][j], + re[i*sideLength+j], im[i*sideLength+j]); + } + } - if (lineNumber % progressInterval == 0) { - double averageTime = (totalTime / 1e6) / numCalculations; // Average time in milliseconds - System.out.println("ifft: Average execution time after " + numCalculations + " calculations: " + String.format("%.8f", averageTime / 1000) + " s"); + if (lineNumber % progressInterval == 0) { + double averageTime = (totalTime / 1e6) / numCalculations; // Average time in milliseconds + System.out.println("fft2d: Average execution time after " + numCalculations + " calculations: " + String.format("%.8f", averageTime/1000) + " s"); + } } + + reader.close(); + System.out.println("fft2d: Finished processing " + lineNumber + " lines.\n Average execution time: " + String.format("%.8f", (totalTime / 1e6 / numCalculations)/1000) + " s"); } - reader.close(); -} -private void validateComplexIFftResults(double[][] expected, double[][][] actualResult, int lineNumber) { - int length = expected[0].length; - for (int i = 0; i < length; i++) { - double realActual = actualResult[0][0][i]; - double imagActual = actualResult[1][0][i]; - assertEquals("Mismatch in real part at index " + i + " in line " + lineNumber, expected[0][i], realActual, 1e-9); - assertEquals("Mismatch in imaginary part at index " + i + " in line " + lineNumber, expected[1][i], imagActual, 1e-9); - } - if (lineNumber % progressInterval == 0) { - System.out.println("ifft(complex input): Finished processing line " + lineNumber); - } -} - - -@Test -public void testFft2dWithNumpyData() throws IOException { - String filename = "complex_fft_2d_data.csv"; // path to your 2D FFT data file - BufferedReader reader = new BufferedReader(new FileReader(filename)); - String line; - int lineNumber = 0; - long totalTime = 0; // Total time for all FFT 2D computations - int numCalculations = 0; // Number of FFT 2D computations - - while ((line = reader.readLine()) != null) { - lineNumber++; - String[] values = line.split(","); - int halfLength = values.length / 4; - int sideLength = (int) Math.sqrt(halfLength); // Assuming square matrix - - double[][][] input = new double[2][sideLength][sideLength]; - double[][][] expected = new double[2][sideLength][sideLength]; - - for (int i = 0; i < halfLength; i++) { - int row = i / sideLength; - int col = i % sideLength; - input[0][row][col] = Double.parseDouble(values[i]); - input[1][row][col] = Double.parseDouble(values[i + halfLength]); - expected[0][row][col] = Double.parseDouble(values[i + 2 * halfLength]); - expected[1][row][col] = Double.parseDouble(values[i + 3 * halfLength]); - } + @Test + public void testIfft2dWithNumpyData() throws IOException { + String filename = "complex_ifft_2d_data.csv"; // path to your 2D IFFT data file + String path = "./src/test/java/org/apache/sysds/test/component/matrix/"; - long startTime = System.nanoTime(); - double[][][] javaFftResult = fft(input, false); // Use your fft2d implementation - long endTime = System.nanoTime(); - totalTime += (endTime - startTime); - numCalculations++; - - for (int i = 0; i < sideLength; i++) { - for (int j = 0; j < sideLength; j++) { - assertComplexEquals("Mismatch at [" + i + "][" + j + "] in line " + lineNumber, - expected[0][i][j], expected[1][i][j], - javaFftResult[0][i][j], javaFftResult[1][i][j]); + BufferedReader reader = new BufferedReader(new FileReader(path+filename)); + String line; + int lineNumber = 0; + long totalTime = 0; // Total time for all IFFT 2D computations + int numCalculations = 0; // Number of IFFT 2D computations + + while ((line = reader.readLine()) != null) { + lineNumber++; + String[] values = line.split(","); + int halfLength = values.length / 4; + int sideLength = (int) Math.sqrt(halfLength); // Assuming square matrix + + // Real and imaginary parts + double[] re = new double[sideLength*sideLength]; + double[] im = new double[sideLength*sideLength]; + + double[][][] expected = new double[2][sideLength][sideLength]; + + for (int i = 0; i < halfLength; i++) { + int row = i / sideLength; + int col = i % sideLength; + + re[row*sideLength+col] = Double.parseDouble(values[i]); + im[row*sideLength+col] = Double.parseDouble(values[i + halfLength]); + + expected[0][row][col] = Double.parseDouble(values[i + 2 * halfLength]); + expected[1][row][col] = Double.parseDouble(values[i + 3 * halfLength]); } - } - if (lineNumber % progressInterval == 0) { - double averageTime = (totalTime / 1e6) / numCalculations; // Average time in milliseconds - System.out.println("fft2d: Average execution time after " + numCalculations + " calculations: " + String.format("%.8f", averageTime/1000) + " s"); - } - } + long startTime = System.nanoTime(); - reader.close(); - System.out.println("fft2d: Finished processing " + lineNumber + " lines.\n Average execution time: " + String.format("%.8f", (totalTime / 1e6 / numCalculations)/1000) + " s"); -} - - -@Test -public void testIfft2dWithNumpyData() throws IOException { - String filename = "complex_ifft_2d_data.csv"; // path to your 2D IFFT data file - BufferedReader reader = new BufferedReader(new FileReader(filename)); - String line; - int lineNumber = 0; - long totalTime = 0; // Total time for all IFFT 2D computations - int numCalculations = 0; // Number of IFFT 2D computations - - while ((line = reader.readLine()) != null) { - lineNumber++; - String[] values = line.split(","); - int halfLength = values.length / 4; - int sideLength = (int) Math.sqrt(halfLength); // Assuming square matrix - - double[][][] input = new double[2][sideLength][sideLength]; - double[][][] expected = new double[2][sideLength][sideLength]; - - for (int i = 0; i < halfLength; i++) { - int row = i / sideLength; - int col = i % sideLength; - input[0][row][col] = Double.parseDouble(values[i]); - input[1][row][col] = Double.parseDouble(values[i + halfLength]); - expected[0][row][col] = Double.parseDouble(values[i + 2 * halfLength]); - expected[1][row][col] = Double.parseDouble(values[i + 3 * halfLength]); - } + // Use your ifft2d implementation + ifft(re, im, sideLength, sideLength); - long startTime = System.nanoTime(); - double[][][] javaIfftResult = fft(input, true); // Use your ifft2d implementation - long endTime = System.nanoTime(); - if(lineNumber > 1000){ - totalTime += (endTime - startTime); - numCalculations++; - } + long endTime = System.nanoTime(); + if(lineNumber > 1000){ + totalTime += (endTime - startTime); + numCalculations++; + } - for (int i = 0; i < sideLength; i++) { - for (int j = 0; j < sideLength; j++) { - assertComplexEquals("Mismatch at [" + i + "][" + j + "] in line " + lineNumber, - expected[0][i][j], expected[1][i][j], - javaIfftResult[0][i][j], javaIfftResult[1][i][j]); + for (int i = 0; i < sideLength; i++) { + for (int j = 0; j < sideLength; j++) { + assertComplexEquals("Mismatch at [" + i + "][" + j + "] in line " + lineNumber, + expected[0][i][j], expected[1][i][j], + re[i*sideLength+j], im[i*sideLength+j]); + } } - } - if (lineNumber % progressInterval == 0) { - System.out.println("fft2d/ifft2d: Finished processing line " + lineNumber); - double averageTime = (totalTime / 1e6) / numCalculations; // Average time in milliseconds - System.out.println("Ifft2d Average execution time after " + numCalculations + " calculations: " + String.format("%.8f", averageTime/1000) + " s"); + if (lineNumber % progressInterval == 0) { + System.out.println("fft2d/ifft2d: Finished processing line " + lineNumber); + double averageTime = (totalTime / 1e6) / numCalculations; // Average time in milliseconds + System.out.println("Ifft2d Average execution time after " + numCalculations + " calculations: " + String.format("%.8f", averageTime/1000) + " s"); + } } - } - reader.close(); - System.out.println("ifft2d: Finished processing " + lineNumber + " lines.\n Average execution time: " + String.format("%.8f", (totalTime / 1e6 / numCalculations)/1000) + " ms"); -} + reader.close(); + System.out.println("ifft2d: Finished processing " + lineNumber + " lines.\n Average execution time: " + String.format("%.8f", (totalTime / 1e6 / numCalculations)/1000) + " ms"); + } // Helper method for asserting equality with a tolerance private static void assertEquals(String message, double expected, double actual, double tolerance) { diff --git a/src/test/java/org/apache/sysds/test/component/matrix/FourierTestWithFilesOld.java b/src/test/java/org/apache/sysds/test/component/matrix/FourierTestWithFilesOld.java new file mode 100644 index 00000000000..3a7fff1b0b5 --- /dev/null +++ b/src/test/java/org/apache/sysds/test/component/matrix/FourierTestWithFilesOld.java @@ -0,0 +1,384 @@ +package org.apache.sysds.test.component.matrix; + +import org.apache.sysds.runtime.matrix.data.*; +import org.junit.*; + +import java.io.*; + +import static org.apache.sysds.runtime.matrix.data.LibMatrixFourierOld.fft_old; +import static org.apache.sysds.runtime.matrix.data.LibMatrixFourierOld.fft_one_dim_old; +import static org.junit.Assert.assertTrue; + +public class FourierTestWithFilesOld { + + int progressInterval = 5000; + + // prior to executing the following tests it is necessary to run the Numpy Script in FourierTestData.py + // and add the generated files to the root of the project. + @Test + public void testFftWithNumpyData() throws IOException { + String filename = "fft_data.csv"; // Path to your CSV file + String path = "./src/test/java/org/apache/sysds/test/component/matrix/"; + + BufferedReader reader = new BufferedReader(new FileReader(path+filename)); + String line; + int lineNumber = 0; + long totalTime = 0; // Total time for all FFT computations + int numCalculations = 0; // Number of FFT computations + + while ((line = reader.readLine()) != null) { + lineNumber++; + + String[] values = line.split(","); + int n = values.length / 3; + double[][][] input = new double[2][1][n]; + double[][] expected = new double[2][n]; // First row for real, second row for imaginary parts + + for (int i = 0; i < n; i++) { + input[0][0][i] = Double.parseDouble(values[i]); + expected[0][i] = Double.parseDouble(values[n + i]); // Real part + expected[1][i] = Double.parseDouble(values[n * 2 + i]); // Imaginary part + } + + long startTime = System.nanoTime(); + MatrixBlock[] actualBlocks = fft_old(input); + long endTime = System.nanoTime(); + + if(lineNumber > 1000){ + totalTime += (endTime - startTime); + numCalculations++; + + if (numCalculations % progressInterval == 0) { + double averageTime = (totalTime / 1e6) / numCalculations; // Average time in milliseconds + System.out.println("fft(double[][][] in): Average execution time after " + numCalculations + " calculations: " + String.format("%.8f", averageTime/1000) + " s"); + } + } + + // Validate the FFT results + validateFftResults(expected, actualBlocks, lineNumber); + } + + reader.close(); + + } + + private void validateFftResults(double[][] expected, MatrixBlock[] actualBlocks, int lineNumber) { + int length = expected[0].length; + for (int i = 0; i < length; i++) { + double realActual = actualBlocks[0].getValueDenseUnsafe(0, i); + double imagActual = actualBlocks[1].getValueDenseUnsafe(0, i); + assertEquals("Mismatch in real part at index " + i + " in line " + lineNumber, expected[0][i], realActual, 1e-9); + assertEquals("Mismatch in imaginary part at index " + i + " in line " + lineNumber, expected[1][i], imagActual, 1e-9); + } + if(lineNumber % progressInterval == 0){ + System.out.println("fft(double[][][] in): Finished processing line " + lineNumber); + } + + } + + @Test + public void testFftExecutionTime() throws IOException { + String filename = "fft_data.csv"; // Path to your CSV file + BufferedReader reader = new BufferedReader(new FileReader(filename)); + String line; + int lineNumber = 0; + long totalTime = 0; // Total time for all FFT computations + int numCalculations = 0; // Number of FFT computations + + while ((line = reader.readLine()) != null) { + lineNumber++; + String[] values = line.split(","); + int n = values.length / 3; + double[][][] input = new double[2][1][n]; + + for (int i = 0; i < n; i++) { + input[0][0][i] = Double.parseDouble(values[i]); // Real part + input[1][0][i] = Double.parseDouble(values[n + i]); // Imaginary part + } + + long startTime = System.nanoTime(); + fft_old(input, false); + long endTime = System.nanoTime(); + if(lineNumber > 1000){ + totalTime += (endTime - startTime); + numCalculations++; + + if (numCalculations % progressInterval == 0) { + double averageTime = (totalTime / 1e6) / numCalculations; // Average time in milliseconds + System.out.println("fft_old(double[][][] in, boolean calcInv) Average execution time after " + numCalculations + " calculations: " + String.format("%.8f", averageTime/1000) + " s"); + } + } + } + + reader.close(); + } + + @Test + public void testFftExecutionTimeOfOneDimFFT() throws IOException { + String filename = "fft_data.csv"; // Path to your CSV file + String path = "./src/test/java/org/apache/sysds/test/component/matrix/"; + + BufferedReader reader = new BufferedReader(new FileReader(path+filename)); + String line; + int lineNumber = 0; + long totalTime = 0; // Total time for all FFT computations + int numCalculations = 0; // Number of FFT computations + + while ((line = reader.readLine()) != null) { + lineNumber++; + String[] values = line.split(","); + int n = values.length / 2; + double[][] input = new double[2][n]; // First row for real, second row for imaginary parts + + for (int i = 0; i < n; i++) { + input[0][i] = Double.parseDouble(values[i]); // Real part + input[1][i] = Double.parseDouble(values[n + i]); // Imaginary part + } + + long startTime = System.nanoTime(); + // one dimensional + fft_one_dim_old(input); + + long endTime = System.nanoTime(); + if(lineNumber > 1000){ + totalTime += (endTime - startTime); + numCalculations++; + + if (numCalculations % progressInterval == 0) { + double averageTime = (totalTime / 1e6) / numCalculations; // Average time in milliseconds + System.out.println("fft_one_dim: Average execution time after " + numCalculations + " calculations: " + String.format("%.8f", averageTime/1000) + " s "); + } + } + } + + reader.close(); + } + + + // prior to executing this test it is necessary to run the Numpy Script in FourierTestData.py and add the generated file to the root of the project. + @Test + public void testIfftWithRealNumpyData() throws IOException { + String filename = "ifft_data.csv"; // Path to your CSV file + BufferedReader reader = new BufferedReader(new FileReader(filename)); + String line; + int lineNumber = 0; + + while ((line = reader.readLine()) != null) { + lineNumber++; + String[] values = line.split(","); + int n = values.length / 3; + double[][][] input = new double[2][1][n]; + double[][] expected = new double[2][n]; // First row for real, second row for imaginary parts + + for (int i = 0; i < n; i++) { + input[0][0][i] = Double.parseDouble(values[i]); // Real part of input + // Imaginary part of input is assumed to be 0 + expected[0][i] = Double.parseDouble(values[n + i]); // Real part of expected output + expected[1][i] = Double.parseDouble(values[n * 2 + i]); // Imaginary part of expected output + } + + double[][][] actualResult = fft_old(input, true); // Perform IFFT + + // Validate the IFFT results + validateFftResults(expected, actualResult, lineNumber); + } + + reader.close(); + } + + private void validateFftResults(double[][] expected, double[][][] actualResult, int lineNumber) { + int length = expected[0].length; + for (int i = 0; i < length; i++) { + double realActual = actualResult[0][0][i]; + double imagActual = actualResult[1][0][i]; + assertEquals("Mismatch in real part at index " + i + " in line " + lineNumber, expected[0][i], realActual, 1e-9); + assertEquals("Mismatch in imaginary part at index " + i + " in line " + lineNumber, expected[1][i], imagActual, 1e-9); + } + if(lineNumber % progressInterval == 0){ + System.out.println("ifft(real input): Finished processing line " + lineNumber); + } + + } + + + + @Test + public void testIfftWithComplexNumpyData() throws IOException { + String filename = "complex_ifft_data.csv"; // Adjusted path to your IFFT data file with complex inputs + BufferedReader reader = new BufferedReader(new FileReader(filename)); + String line; + int lineNumber = 0; + long totalTime = 0; // Total time for all IFFT computations + int numCalculations = 0; // Number of IFFT computations + + while ((line = reader.readLine()) != null) { + lineNumber++; + String[] values = line.split(","); + int n = values.length / 4; // Adjusted for complex numbers + double[][][] input = new double[2][1][n]; // Real and imaginary parts + double[][] expected = new double[2][n]; // Expected real and imaginary parts + + for (int i = 0; i < n; i++) { + input[0][0][i] = Double.parseDouble(values[i]); // Real part of input + input[1][0][i] = Double.parseDouble(values[i + n]); // Imaginary part of input + expected[0][i] = Double.parseDouble(values[i + 2 * n]); // Expected real part + expected[1][i] = Double.parseDouble(values[i + 3 * n]); // Expected imaginary part + } + + long startTime = System.nanoTime(); + double[][][] actualResult = fft_old(input, true); // Perform IFFT + long endTime = System.nanoTime(); + + if (lineNumber > 1000) { + totalTime += (endTime - startTime); + numCalculations++; + } + + // Validate the IFFT results + validateComplexIFftResults(expected, actualResult, lineNumber); + + if (lineNumber % progressInterval == 0) { + double averageTime = (totalTime / 1e6) / numCalculations; // Average time in milliseconds + System.out.println("ifft: Average execution time after " + numCalculations + " calculations: " + String.format("%.8f", averageTime / 1000) + " s"); + } + } + + reader.close(); + } + + private void validateComplexIFftResults(double[][] expected, double[][][] actualResult, int lineNumber) { + int length = expected[0].length; + for (int i = 0; i < length; i++) { + double realActual = actualResult[0][0][i]; + double imagActual = actualResult[1][0][i]; + assertEquals("Mismatch in real part at index " + i + " in line " + lineNumber, expected[0][i], realActual, 1e-9); + assertEquals("Mismatch in imaginary part at index " + i + " in line " + lineNumber, expected[1][i], imagActual, 1e-9); + } + if (lineNumber % progressInterval == 0) { + System.out.println("ifft(complex input): Finished processing line " + lineNumber); + } + } + + + @Test + public void testFft2dWithNumpyData() throws IOException { + String filename = "complex_fft_2d_data.csv"; // path to your 2D FFT data file + BufferedReader reader = new BufferedReader(new FileReader(filename)); + String line; + int lineNumber = 0; + long totalTime = 0; // Total time for all FFT 2D computations + int numCalculations = 0; // Number of FFT 2D computations + + while ((line = reader.readLine()) != null) { + lineNumber++; + String[] values = line.split(","); + int halfLength = values.length / 4; + int sideLength = (int) Math.sqrt(halfLength); // Assuming square matrix + + double[][][] input = new double[2][sideLength][sideLength]; + double[][][] expected = new double[2][sideLength][sideLength]; + + for (int i = 0; i < halfLength; i++) { + int row = i / sideLength; + int col = i % sideLength; + input[0][row][col] = Double.parseDouble(values[i]); + input[1][row][col] = Double.parseDouble(values[i + halfLength]); + expected[0][row][col] = Double.parseDouble(values[i + 2 * halfLength]); + expected[1][row][col] = Double.parseDouble(values[i + 3 * halfLength]); + } + + long startTime = System.nanoTime(); + double[][][] javaFftResult = fft_old(input, false); // Use your fft2d implementation + long endTime = System.nanoTime(); + totalTime += (endTime - startTime); + numCalculations++; + + for (int i = 0; i < sideLength; i++) { + for (int j = 0; j < sideLength; j++) { + assertComplexEquals("Mismatch at [" + i + "][" + j + "] in line " + lineNumber, + expected[0][i][j], expected[1][i][j], + javaFftResult[0][i][j], javaFftResult[1][i][j]); + } + } + + if (lineNumber % progressInterval == 0) { + double averageTime = (totalTime / 1e6) / numCalculations; // Average time in milliseconds + System.out.println("fft2d: Average execution time after " + numCalculations + " calculations: " + String.format("%.8f", averageTime/1000) + " s"); + } + } + + reader.close(); + System.out.println("fft2d: Finished processing " + lineNumber + " lines.\n Average execution time: " + String.format("%.8f", (totalTime / 1e6 / numCalculations)/1000) + " s"); + } + + + @Test + public void testIfft2dWithNumpyData() throws IOException { + String filename = "complex_ifft_2d_data.csv"; // path to your 2D IFFT data file + String path = "./src/test/java/org/apache/sysds/test/component/matrix/"; + + BufferedReader reader = new BufferedReader(new FileReader(path+filename)); + String line; + int lineNumber = 0; + long totalTime = 0; // Total time for all IFFT 2D computations + int numCalculations = 0; // Number of IFFT 2D computations + + while ((line = reader.readLine()) != null) { + lineNumber++; + String[] values = line.split(","); + int halfLength = values.length / 4; + int sideLength = (int) Math.sqrt(halfLength); // Assuming square matrix + + double[][][] input = new double[2][sideLength][sideLength]; + double[][][] expected = new double[2][sideLength][sideLength]; + + for (int i = 0; i < halfLength; i++) { + int row = i / sideLength; + int col = i % sideLength; + input[0][row][col] = Double.parseDouble(values[i]); + input[1][row][col] = Double.parseDouble(values[i + halfLength]); + expected[0][row][col] = Double.parseDouble(values[i + 2 * halfLength]); + expected[1][row][col] = Double.parseDouble(values[i + 3 * halfLength]); + } + + long startTime = System.nanoTime(); + double[][][] javaIfftResult = fft_old(input, true); // Use your ifft2d implementation + long endTime = System.nanoTime(); + if(lineNumber > 1000){ + totalTime += (endTime - startTime); + numCalculations++; + } + + for (int i = 0; i < sideLength; i++) { + for (int j = 0; j < sideLength; j++) { + assertComplexEquals("Mismatch at [" + i + "][" + j + "] in line " + lineNumber, + expected[0][i][j], expected[1][i][j], + javaIfftResult[0][i][j], javaIfftResult[1][i][j]); + } + } + + if (lineNumber % progressInterval == 0) { + System.out.println("fft2d/ifft2d: Finished processing line " + lineNumber); + double averageTime = (totalTime / 1e6) / numCalculations; // Average time in milliseconds + System.out.println("Ifft2d Average execution time after " + numCalculations + " calculations: " + String.format("%.8f", averageTime/1000) + " s"); + } + } + + reader.close(); + System.out.println("ifft2d: Finished processing " + lineNumber + " lines.\n Average execution time: " + String.format("%.8f", (totalTime / 1e6 / numCalculations)/1000) + " ms"); + } + + // Helper method for asserting equality with a tolerance + private static void assertEquals(String message, double expected, double actual, double tolerance) { + assertTrue(message + " - Expected: " + expected + ", Actual: " + actual, Math.abs(expected - actual) <= tolerance); + } + + private void assertComplexEquals(String message, double expectedReal, double expectedImag, double actualReal, double actualImag) { + final double EPSILON = 1e-9; + assertTrue(message + " - Mismatch in real part. Expected: " + expectedReal + ", Actual: " + actualReal, + Math.abs(expectedReal - actualReal) <= EPSILON); + assertTrue(message + " - Mismatch in imaginary part. Expected: " + expectedImag + ", Actual: " + actualImag, + Math.abs(expectedImag - actualImag) <= EPSILON); + } + +} From 602c749db20e7cc4c0967a903cbcdf1385c3b2af Mon Sep 17 00:00:00 2001 From: Jessica Priebe Date: Wed, 17 Jan 2024 15:04:47 +0100 Subject: [PATCH 031/133] removed old fft and ifft + tests --- .../matrix/data/LibMatrixFourierOld.java | 238 ----------- .../test/component/matrix/FourierOldTest.java | 101 ----- .../matrix/FourierTestWithFilesOld.java | 384 ------------------ 3 files changed, 723 deletions(-) delete mode 100644 src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourierOld.java delete mode 100644 src/test/java/org/apache/sysds/test/component/matrix/FourierOldTest.java delete mode 100644 src/test/java/org/apache/sysds/test/component/matrix/FourierTestWithFilesOld.java diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourierOld.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourierOld.java deleted file mode 100644 index 832d6955fec..00000000000 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourierOld.java +++ /dev/null @@ -1,238 +0,0 @@ -package org.apache.sysds.runtime.matrix.data; - -import org.apache.commons.math3.util.FastMath; - -import java.util.Arrays; - -public class LibMatrixFourierOld { - - /** - * Function to perform Fast Fourier Transformation - */ - - public static MatrixBlock[] fft_old(MatrixBlock re, MatrixBlock im){ - - int rows = re.getNumRows(); - int cols = re.getNumColumns(); - - double[][][] in = new double[2][rows][cols]; - in[0] = convertToArray(re); - in[1] = convertToArray(im); - - double[][][] res = fft_old(in, false); - - return convertToMatrixBlocks(res); - } - - public static MatrixBlock[] ifft_old(MatrixBlock re, MatrixBlock im){ - - int rows = re.getNumRows(); - int cols = re.getNumColumns(); - - double[][][] in = new double[2][rows][cols]; - in[0] = convertToArray(re); - in[1] = convertToArray(im); - - double[][][] res = fft_old(in, true); - - return convertToMatrixBlocks(res); - } - - public static double[][][] fft_old(double[][][] in, boolean calcInv){ - - int rows = in[0].length; - int cols = in[0][0].length; - - double[][][] res = new double[2][rows][cols]; - - for(int i = 0; i < rows; i++){ - // use fft or ifft on each row - double[][] res_row = calcInv? ifft_one_dim_old(get_complex_row(in, i)) : fft_one_dim_old(get_complex_row(in, i)); - - // set res row - for (int j = 0; j < cols; j++){ - for( int k = 0; k < 2; k++){ - res[k][i][j] = res_row[k][j]; - } - } - } - - if(rows == 1) return res; - - for(int j = 0; j < cols; j++){ - // use fft on each col - double[][] res_col = calcInv? ifft_one_dim_old(get_complex_col(res, j)) : fft_one_dim_old(get_complex_col(res, j)); - - // set res col - for (int i = 0; i < rows; i++){ - for( int k = 0; k < 2; k++){ - res[k][i][j] = res_col[k][i]; - } - } - } - - return res; - } - - public static double[][] fft_one_dim_old(double[][] in){ - // 1st row real part, 2nd row imaginary part - if(in == null || in.length != 2 || in[0].length != in[1].length) throw new RuntimeException("in false dimensions"); - - int cols = in[0].length; - if(cols == 1) return in; - - double angle = -2* FastMath.PI/cols; - - // split values depending on index - double[][] even = new double[2][cols/2]; - double[][] odd = new double[2][cols/2]; - - for(int i = 0; i < 2; i++){ - for (int j = 0; j < cols/2; j++){ - even[i][j] = in[i][j*2]; - odd[i][j] = in[i][j*2+1]; - } - } - - double[][] res_even = fft_one_dim_old(even); - double[][] res_odd = fft_one_dim_old(odd); - - double[][] res = new double[2][cols]; - - for(int j=0; j < cols/2; j++){ - double[] omega_pow = new double[]{FastMath.cos(j*angle), FastMath.sin(j*angle)}; - - // m = omega * res_odd[j] - double[] m = new double[]{ - omega_pow[0] * res_odd[0][j] - omega_pow[1] * res_odd[1][j], - omega_pow[0] * res_odd[1][j] + omega_pow[1] * res_odd[0][j]}; - - // res[j] = res_even + m; - // res[j+cols/2] = res_even - m; - for(int i = 0; i < 2; i++){ - res[i][j] = res_even[i][j] + m[i]; - res[i][j+cols/2] = res_even[i][j] - m[i]; - } - } - - return res; - - } - - public static double[][] ifft_one_dim_old(double[][] in) { - - // cols[0] is real part, cols[1] is imaginary part - int cols = in[0].length; - - // conjugate input - in[1] = Arrays.stream(in[1]).map(i -> -i).toArray(); - - // apply fft - double[][] res = fft_one_dim_old(in); - - // conjugate and scale result - res[0] = Arrays.stream(res[0]).map(i -> i/cols).toArray(); - res[1] = Arrays.stream(res[1]).map(i -> -i/cols).toArray(); - - return res; - } - - private static MatrixBlock[] convertToMatrixBlocks(double[][][] in){ - - int cols = in[0][0].length; - int rows = in[0].length; - - double[] flattened_re = Arrays.stream(in[0]).flatMapToDouble(Arrays::stream).toArray(); - double[] flattened_im = new double[rows*cols]; - if(in.length > 1){ - flattened_im = Arrays.stream(in[1]).flatMapToDouble(Arrays::stream).toArray(); - } - - MatrixBlock re = new MatrixBlock(rows, cols, flattened_re); - MatrixBlock im = new MatrixBlock(rows, cols, flattened_im); - - return new MatrixBlock[]{re, im}; - } - - private static MatrixBlock getZeroMatrixBlock(int rows, int cols){ - - return new MatrixBlock(rows, cols, new double[cols*rows]); - - } - - private static double[][] convertToArray(MatrixBlock in){ - - int rows = in.getNumRows(); - int cols = in.getNumColumns(); - - double[][] out = new double[rows][cols]; - for(int i = 0; i < rows; i++){ - out[i] = Arrays.copyOfRange(in.getDenseBlockValues(), i * cols, (i+1) * cols); - } - - return out; - } - private static double[][][] convertToArray(MatrixBlock[] in){ - - int rows = in[0].getNumRows(); - int cols = in[0].getNumColumns(); - - double[][][] out = new double[2][rows][cols]; - for(int k = 0; k < 2; k++){ - for(int i = 0; i < rows; i++){ - out[k][i] = Arrays.copyOfRange(in[k].getDenseBlockValues(), i * cols, (i+1) * cols); - } - } - - return out; - } - - public static double[][] get_complex_row(double[][][] in, int i){ - - int cols = in[0][0].length; - - double[][] row = new double[2][cols]; - // get row - for (int j = 0; j < cols; j++){ - for( int k = 0; k < 2; k++){ - row[k][j] = in[k][i][j]; - } - } - return row; - } - public static double[][] get_complex_col(double[][][] in, int j){ - - int rows = in[0].length; - - double[][] col = new double[2][rows]; - // get row - for (int i = 0; i < rows; i++){ - for( int k = 0; k < 2; k++){ - col[k][i] = in[k][i][j]; - } - } - return col; - } - - public static boolean isPowerOfTwo(int n){ - return (n != 0) && ((n & (n - 1)) == 0); - } - - public static MatrixBlock[] fft_old(double[] in){ - double[][][] arr = new double[2][1][in.length]; - arr[0][0] = in; - return fft_old(convertToMatrixBlocks(arr)); - } - - public static MatrixBlock[] fft_old(double[][][] in){ - return fft_old(convertToMatrixBlocks(in)); - } - - public static MatrixBlock[] fft_old(MatrixBlock[] in){ - return fft_old(in[0], in[1]); - } - - public static MatrixBlock[] fft_old(MatrixBlock re){ - return fft_old(re, getZeroMatrixBlock(re.getNumRows(), re.getNumColumns())); - } -} diff --git a/src/test/java/org/apache/sysds/test/component/matrix/FourierOldTest.java b/src/test/java/org/apache/sysds/test/component/matrix/FourierOldTest.java deleted file mode 100644 index 3168a00e5f9..00000000000 --- a/src/test/java/org/apache/sysds/test/component/matrix/FourierOldTest.java +++ /dev/null @@ -1,101 +0,0 @@ -package org.apache.sysds.test.component.matrix; - -import org.apache.sysds.runtime.matrix.data.MatrixBlock; -import org.junit.Test; - -import static org.apache.sysds.runtime.matrix.data.LibMatrixFourierOld.fft_one_dim_old; -import static org.apache.sysds.runtime.matrix.data.LibMatrixFourierOld.ifft_one_dim_old; -import static org.apache.sysds.runtime.matrix.data.LibMatrixFourierOld.fft_old; -import static org.apache.sysds.runtime.matrix.data.LibMatrixFourierOld.ifft_old; -import static org.junit.Assert.assertArrayEquals; - -public class FourierOldTest { - - @Test - public void simple_test_one_dim() { - // 1st row real part, 2nd row imaginary part - double[][] in = {{0, 18, -15, 3},{0, 0, 0, 0}}; - double[][] expected = {{6, 15, -36, 15},{0, -15, 0, 15}}; - - double[][] res = fft_one_dim_old(in); - - assertArrayEquals(expected[0], res[0], 0.0001); - assertArrayEquals(expected[1], res[1], 0.0001); - } - - @Test - public void simple_test_two_dim() { - // tested with numpy - double[][][] in = {{{0, 18},{-15, 3}},{{0, 0},{0, 0}}}; - - double[][][] expected = {{{6, -36},{30, 0}},{{0, 0},{0, 0}}}; - - double[][][] res = fft_old(in, false); - - for(int k = 0; k < 2 ; k++){ - for(int i = 0; i < res[0].length; i++) { - assertArrayEquals(expected[k][i], res[k][i], 0.0001); - } - } - } - - @Test - public void simple_test_one_dim_ifft() { - - double[][] in = {{1, -2, 3, -4},{0, 0, 0, 0}}; - - double[][] res_fft = fft_one_dim_old(in); - double[][] res = ifft_one_dim_old(res_fft); - - assertArrayEquals(in[0], res[0], 0.0001); - assertArrayEquals(in[1], res[1], 0.0001); - } - - @Test - public void matrix_block_one_dim_test(){ - - double[] in = {0, 18, -15, 3}; - - double[] expected_re = {6,15,-36,15}; - double[] expected_im = {0,-15,0,15}; - - MatrixBlock[] res = fft_old(in); - double[] res_re = res[0].getDenseBlockValues(); - double[] res_im = res[1].getDenseBlockValues(); - - assertArrayEquals(expected_re, res_re, 0.0001); - assertArrayEquals(expected_im, res_im, 0.0001); - } - @Test - public void matrix_block_two_dim_test(){ - - double[][][] in = {{{0, 18},{-15, 3}}}; - - double[] flattened_expected_re = {6,-36, 30,0}; - double[] flattened_expected_im = {0,0,0,0}; - - MatrixBlock[] res = fft_old(in); - double[] res_re = res[0].getDenseBlockValues(); - double[] res_im = res[1].getDenseBlockValues(); - - assertArrayEquals(flattened_expected_re, res_re, 0.0001); - assertArrayEquals(flattened_expected_im, res_im, 0.0001); - } - - @Test - public void test_ifft_two_dim_matrixBlock() { - - MatrixBlock re = new MatrixBlock(2, 2, new double[]{6,-36, 30, 0}); - MatrixBlock im = new MatrixBlock(2, 2, new double[]{0, 0, 0, 0}); - - double[] expected_re = {0, 18, -15, 3}; - double[] expected_im = {0, 0, 0, 0}; - - MatrixBlock[] res = ifft_old(re, im); - - assertArrayEquals(expected_re, res[0].getDenseBlockValues(), 0.0001); - assertArrayEquals(expected_im, res[1].getDenseBlockValues(), 0.0001); - - } - -} diff --git a/src/test/java/org/apache/sysds/test/component/matrix/FourierTestWithFilesOld.java b/src/test/java/org/apache/sysds/test/component/matrix/FourierTestWithFilesOld.java deleted file mode 100644 index 3a7fff1b0b5..00000000000 --- a/src/test/java/org/apache/sysds/test/component/matrix/FourierTestWithFilesOld.java +++ /dev/null @@ -1,384 +0,0 @@ -package org.apache.sysds.test.component.matrix; - -import org.apache.sysds.runtime.matrix.data.*; -import org.junit.*; - -import java.io.*; - -import static org.apache.sysds.runtime.matrix.data.LibMatrixFourierOld.fft_old; -import static org.apache.sysds.runtime.matrix.data.LibMatrixFourierOld.fft_one_dim_old; -import static org.junit.Assert.assertTrue; - -public class FourierTestWithFilesOld { - - int progressInterval = 5000; - - // prior to executing the following tests it is necessary to run the Numpy Script in FourierTestData.py - // and add the generated files to the root of the project. - @Test - public void testFftWithNumpyData() throws IOException { - String filename = "fft_data.csv"; // Path to your CSV file - String path = "./src/test/java/org/apache/sysds/test/component/matrix/"; - - BufferedReader reader = new BufferedReader(new FileReader(path+filename)); - String line; - int lineNumber = 0; - long totalTime = 0; // Total time for all FFT computations - int numCalculations = 0; // Number of FFT computations - - while ((line = reader.readLine()) != null) { - lineNumber++; - - String[] values = line.split(","); - int n = values.length / 3; - double[][][] input = new double[2][1][n]; - double[][] expected = new double[2][n]; // First row for real, second row for imaginary parts - - for (int i = 0; i < n; i++) { - input[0][0][i] = Double.parseDouble(values[i]); - expected[0][i] = Double.parseDouble(values[n + i]); // Real part - expected[1][i] = Double.parseDouble(values[n * 2 + i]); // Imaginary part - } - - long startTime = System.nanoTime(); - MatrixBlock[] actualBlocks = fft_old(input); - long endTime = System.nanoTime(); - - if(lineNumber > 1000){ - totalTime += (endTime - startTime); - numCalculations++; - - if (numCalculations % progressInterval == 0) { - double averageTime = (totalTime / 1e6) / numCalculations; // Average time in milliseconds - System.out.println("fft(double[][][] in): Average execution time after " + numCalculations + " calculations: " + String.format("%.8f", averageTime/1000) + " s"); - } - } - - // Validate the FFT results - validateFftResults(expected, actualBlocks, lineNumber); - } - - reader.close(); - - } - - private void validateFftResults(double[][] expected, MatrixBlock[] actualBlocks, int lineNumber) { - int length = expected[0].length; - for (int i = 0; i < length; i++) { - double realActual = actualBlocks[0].getValueDenseUnsafe(0, i); - double imagActual = actualBlocks[1].getValueDenseUnsafe(0, i); - assertEquals("Mismatch in real part at index " + i + " in line " + lineNumber, expected[0][i], realActual, 1e-9); - assertEquals("Mismatch in imaginary part at index " + i + " in line " + lineNumber, expected[1][i], imagActual, 1e-9); - } - if(lineNumber % progressInterval == 0){ - System.out.println("fft(double[][][] in): Finished processing line " + lineNumber); - } - - } - - @Test - public void testFftExecutionTime() throws IOException { - String filename = "fft_data.csv"; // Path to your CSV file - BufferedReader reader = new BufferedReader(new FileReader(filename)); - String line; - int lineNumber = 0; - long totalTime = 0; // Total time for all FFT computations - int numCalculations = 0; // Number of FFT computations - - while ((line = reader.readLine()) != null) { - lineNumber++; - String[] values = line.split(","); - int n = values.length / 3; - double[][][] input = new double[2][1][n]; - - for (int i = 0; i < n; i++) { - input[0][0][i] = Double.parseDouble(values[i]); // Real part - input[1][0][i] = Double.parseDouble(values[n + i]); // Imaginary part - } - - long startTime = System.nanoTime(); - fft_old(input, false); - long endTime = System.nanoTime(); - if(lineNumber > 1000){ - totalTime += (endTime - startTime); - numCalculations++; - - if (numCalculations % progressInterval == 0) { - double averageTime = (totalTime / 1e6) / numCalculations; // Average time in milliseconds - System.out.println("fft_old(double[][][] in, boolean calcInv) Average execution time after " + numCalculations + " calculations: " + String.format("%.8f", averageTime/1000) + " s"); - } - } - } - - reader.close(); - } - - @Test - public void testFftExecutionTimeOfOneDimFFT() throws IOException { - String filename = "fft_data.csv"; // Path to your CSV file - String path = "./src/test/java/org/apache/sysds/test/component/matrix/"; - - BufferedReader reader = new BufferedReader(new FileReader(path+filename)); - String line; - int lineNumber = 0; - long totalTime = 0; // Total time for all FFT computations - int numCalculations = 0; // Number of FFT computations - - while ((line = reader.readLine()) != null) { - lineNumber++; - String[] values = line.split(","); - int n = values.length / 2; - double[][] input = new double[2][n]; // First row for real, second row for imaginary parts - - for (int i = 0; i < n; i++) { - input[0][i] = Double.parseDouble(values[i]); // Real part - input[1][i] = Double.parseDouble(values[n + i]); // Imaginary part - } - - long startTime = System.nanoTime(); - // one dimensional - fft_one_dim_old(input); - - long endTime = System.nanoTime(); - if(lineNumber > 1000){ - totalTime += (endTime - startTime); - numCalculations++; - - if (numCalculations % progressInterval == 0) { - double averageTime = (totalTime / 1e6) / numCalculations; // Average time in milliseconds - System.out.println("fft_one_dim: Average execution time after " + numCalculations + " calculations: " + String.format("%.8f", averageTime/1000) + " s "); - } - } - } - - reader.close(); - } - - - // prior to executing this test it is necessary to run the Numpy Script in FourierTestData.py and add the generated file to the root of the project. - @Test - public void testIfftWithRealNumpyData() throws IOException { - String filename = "ifft_data.csv"; // Path to your CSV file - BufferedReader reader = new BufferedReader(new FileReader(filename)); - String line; - int lineNumber = 0; - - while ((line = reader.readLine()) != null) { - lineNumber++; - String[] values = line.split(","); - int n = values.length / 3; - double[][][] input = new double[2][1][n]; - double[][] expected = new double[2][n]; // First row for real, second row for imaginary parts - - for (int i = 0; i < n; i++) { - input[0][0][i] = Double.parseDouble(values[i]); // Real part of input - // Imaginary part of input is assumed to be 0 - expected[0][i] = Double.parseDouble(values[n + i]); // Real part of expected output - expected[1][i] = Double.parseDouble(values[n * 2 + i]); // Imaginary part of expected output - } - - double[][][] actualResult = fft_old(input, true); // Perform IFFT - - // Validate the IFFT results - validateFftResults(expected, actualResult, lineNumber); - } - - reader.close(); - } - - private void validateFftResults(double[][] expected, double[][][] actualResult, int lineNumber) { - int length = expected[0].length; - for (int i = 0; i < length; i++) { - double realActual = actualResult[0][0][i]; - double imagActual = actualResult[1][0][i]; - assertEquals("Mismatch in real part at index " + i + " in line " + lineNumber, expected[0][i], realActual, 1e-9); - assertEquals("Mismatch in imaginary part at index " + i + " in line " + lineNumber, expected[1][i], imagActual, 1e-9); - } - if(lineNumber % progressInterval == 0){ - System.out.println("ifft(real input): Finished processing line " + lineNumber); - } - - } - - - - @Test - public void testIfftWithComplexNumpyData() throws IOException { - String filename = "complex_ifft_data.csv"; // Adjusted path to your IFFT data file with complex inputs - BufferedReader reader = new BufferedReader(new FileReader(filename)); - String line; - int lineNumber = 0; - long totalTime = 0; // Total time for all IFFT computations - int numCalculations = 0; // Number of IFFT computations - - while ((line = reader.readLine()) != null) { - lineNumber++; - String[] values = line.split(","); - int n = values.length / 4; // Adjusted for complex numbers - double[][][] input = new double[2][1][n]; // Real and imaginary parts - double[][] expected = new double[2][n]; // Expected real and imaginary parts - - for (int i = 0; i < n; i++) { - input[0][0][i] = Double.parseDouble(values[i]); // Real part of input - input[1][0][i] = Double.parseDouble(values[i + n]); // Imaginary part of input - expected[0][i] = Double.parseDouble(values[i + 2 * n]); // Expected real part - expected[1][i] = Double.parseDouble(values[i + 3 * n]); // Expected imaginary part - } - - long startTime = System.nanoTime(); - double[][][] actualResult = fft_old(input, true); // Perform IFFT - long endTime = System.nanoTime(); - - if (lineNumber > 1000) { - totalTime += (endTime - startTime); - numCalculations++; - } - - // Validate the IFFT results - validateComplexIFftResults(expected, actualResult, lineNumber); - - if (lineNumber % progressInterval == 0) { - double averageTime = (totalTime / 1e6) / numCalculations; // Average time in milliseconds - System.out.println("ifft: Average execution time after " + numCalculations + " calculations: " + String.format("%.8f", averageTime / 1000) + " s"); - } - } - - reader.close(); - } - - private void validateComplexIFftResults(double[][] expected, double[][][] actualResult, int lineNumber) { - int length = expected[0].length; - for (int i = 0; i < length; i++) { - double realActual = actualResult[0][0][i]; - double imagActual = actualResult[1][0][i]; - assertEquals("Mismatch in real part at index " + i + " in line " + lineNumber, expected[0][i], realActual, 1e-9); - assertEquals("Mismatch in imaginary part at index " + i + " in line " + lineNumber, expected[1][i], imagActual, 1e-9); - } - if (lineNumber % progressInterval == 0) { - System.out.println("ifft(complex input): Finished processing line " + lineNumber); - } - } - - - @Test - public void testFft2dWithNumpyData() throws IOException { - String filename = "complex_fft_2d_data.csv"; // path to your 2D FFT data file - BufferedReader reader = new BufferedReader(new FileReader(filename)); - String line; - int lineNumber = 0; - long totalTime = 0; // Total time for all FFT 2D computations - int numCalculations = 0; // Number of FFT 2D computations - - while ((line = reader.readLine()) != null) { - lineNumber++; - String[] values = line.split(","); - int halfLength = values.length / 4; - int sideLength = (int) Math.sqrt(halfLength); // Assuming square matrix - - double[][][] input = new double[2][sideLength][sideLength]; - double[][][] expected = new double[2][sideLength][sideLength]; - - for (int i = 0; i < halfLength; i++) { - int row = i / sideLength; - int col = i % sideLength; - input[0][row][col] = Double.parseDouble(values[i]); - input[1][row][col] = Double.parseDouble(values[i + halfLength]); - expected[0][row][col] = Double.parseDouble(values[i + 2 * halfLength]); - expected[1][row][col] = Double.parseDouble(values[i + 3 * halfLength]); - } - - long startTime = System.nanoTime(); - double[][][] javaFftResult = fft_old(input, false); // Use your fft2d implementation - long endTime = System.nanoTime(); - totalTime += (endTime - startTime); - numCalculations++; - - for (int i = 0; i < sideLength; i++) { - for (int j = 0; j < sideLength; j++) { - assertComplexEquals("Mismatch at [" + i + "][" + j + "] in line " + lineNumber, - expected[0][i][j], expected[1][i][j], - javaFftResult[0][i][j], javaFftResult[1][i][j]); - } - } - - if (lineNumber % progressInterval == 0) { - double averageTime = (totalTime / 1e6) / numCalculations; // Average time in milliseconds - System.out.println("fft2d: Average execution time after " + numCalculations + " calculations: " + String.format("%.8f", averageTime/1000) + " s"); - } - } - - reader.close(); - System.out.println("fft2d: Finished processing " + lineNumber + " lines.\n Average execution time: " + String.format("%.8f", (totalTime / 1e6 / numCalculations)/1000) + " s"); - } - - - @Test - public void testIfft2dWithNumpyData() throws IOException { - String filename = "complex_ifft_2d_data.csv"; // path to your 2D IFFT data file - String path = "./src/test/java/org/apache/sysds/test/component/matrix/"; - - BufferedReader reader = new BufferedReader(new FileReader(path+filename)); - String line; - int lineNumber = 0; - long totalTime = 0; // Total time for all IFFT 2D computations - int numCalculations = 0; // Number of IFFT 2D computations - - while ((line = reader.readLine()) != null) { - lineNumber++; - String[] values = line.split(","); - int halfLength = values.length / 4; - int sideLength = (int) Math.sqrt(halfLength); // Assuming square matrix - - double[][][] input = new double[2][sideLength][sideLength]; - double[][][] expected = new double[2][sideLength][sideLength]; - - for (int i = 0; i < halfLength; i++) { - int row = i / sideLength; - int col = i % sideLength; - input[0][row][col] = Double.parseDouble(values[i]); - input[1][row][col] = Double.parseDouble(values[i + halfLength]); - expected[0][row][col] = Double.parseDouble(values[i + 2 * halfLength]); - expected[1][row][col] = Double.parseDouble(values[i + 3 * halfLength]); - } - - long startTime = System.nanoTime(); - double[][][] javaIfftResult = fft_old(input, true); // Use your ifft2d implementation - long endTime = System.nanoTime(); - if(lineNumber > 1000){ - totalTime += (endTime - startTime); - numCalculations++; - } - - for (int i = 0; i < sideLength; i++) { - for (int j = 0; j < sideLength; j++) { - assertComplexEquals("Mismatch at [" + i + "][" + j + "] in line " + lineNumber, - expected[0][i][j], expected[1][i][j], - javaIfftResult[0][i][j], javaIfftResult[1][i][j]); - } - } - - if (lineNumber % progressInterval == 0) { - System.out.println("fft2d/ifft2d: Finished processing line " + lineNumber); - double averageTime = (totalTime / 1e6) / numCalculations; // Average time in milliseconds - System.out.println("Ifft2d Average execution time after " + numCalculations + " calculations: " + String.format("%.8f", averageTime/1000) + " s"); - } - } - - reader.close(); - System.out.println("ifft2d: Finished processing " + lineNumber + " lines.\n Average execution time: " + String.format("%.8f", (totalTime / 1e6 / numCalculations)/1000) + " ms"); - } - - // Helper method for asserting equality with a tolerance - private static void assertEquals(String message, double expected, double actual, double tolerance) { - assertTrue(message + " - Expected: " + expected + ", Actual: " + actual, Math.abs(expected - actual) <= tolerance); - } - - private void assertComplexEquals(String message, double expectedReal, double expectedImag, double actualReal, double actualImag) { - final double EPSILON = 1e-9; - assertTrue(message + " - Mismatch in real part. Expected: " + expectedReal + ", Actual: " + actualReal, - Math.abs(expectedReal - actualReal) <= EPSILON); - assertTrue(message + " - Mismatch in imaginary part. Expected: " + expectedImag + ", Actual: " + actualImag, - Math.abs(expectedImag - actualImag) <= EPSILON); - } - -} From d2edaefcbf3f839f992c2b6be7534c92ff752fab Mon Sep 17 00:00:00 2001 From: Jessica Priebe Date: Wed, 17 Jan 2024 15:11:36 +0100 Subject: [PATCH 032/133] stft with double[] array --- .../runtime/matrix/data/LibCommonsMath.java | 7 ++++--- .../runtime/matrix/data/LibMatrixSTFT.java | 19 ++++++++++--------- .../component/matrix/LibMatrixSTFTTest.java | 5 +++-- 3 files changed, 17 insertions(+), 14 deletions(-) diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java index 007a8feddd4..9297e184b7a 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java @@ -49,7 +49,8 @@ import org.apache.sysds.runtime.matrix.operators.BinaryOperator; import org.apache.sysds.runtime.util.DataConverter; -import static org.apache.sysds.runtime.matrix.data.LibMatrixFourierOld.*; +import static org.apache.sysds.runtime.matrix.data.LibMatrixFourier.fft; +import static org.apache.sysds.runtime.matrix.data.LibMatrixFourier.ifft; /** * Library for matrix operations that need invocation of @@ -263,7 +264,7 @@ private static MatrixBlock[] computeFFT(MatrixBlock in) { throw new DMLRuntimeException("Invalid empty block"); //run fft - return fft_old(in); + return fft(in); } private static MatrixBlock[] computeIFFT(MatrixBlock in) { @@ -274,7 +275,7 @@ private static MatrixBlock[] computeIFFT(MatrixBlock in) { MatrixBlock inIm = new MatrixBlock(rows, cols, new double[cols*rows]); //run ifft - return ifft_old(in, inIm); + return ifft(in, inIm); } /** diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixSTFT.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixSTFT.java index a50dcfe85ef..d5fd490377e 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixSTFT.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixSTFT.java @@ -2,9 +2,7 @@ import java.util.Arrays; -import static org.apache.sysds.runtime.matrix.data.LibMatrixFourierOld.fft_old; -import static org.apache.sysds.runtime.matrix.data.LibMatrixFourierOld.fft_one_dim_old; - +import static org.apache.sysds.runtime.matrix.data.LibMatrixFourier.fft; public class LibMatrixSTFT { @@ -54,19 +52,22 @@ public static double[][] one_dim_stft(double[] signal, int windowSize, int overl // Perform FFT on windowed signal int l = windowedSignal.length; - double[][] tmp = new double[2][l]; + + double[] tmp_re = new double[l]; + double[] tmp_im = new double[l]; for (int i = 0; i < l; i++) { - tmp[0][i] = windowedSignal[i]; - tmp[1][i] = 0; + tmp_re[i] = windowedSignal[i]; + tmp_im[i] = 0; } - double[][] fftResult = fft_one_dim_old(tmp); + + fft(tmp_re, tmp_im, 1, l); // Store the FFT result in the output array int startIndex = windowSize * frame; for (int i = 0; i < l; i++) { - stftOutput[0][startIndex + i] = fftResult[0][i]; - stftOutput[1][startIndex + i] = fftResult[1][i]; + stftOutput[0][startIndex + i] = tmp_re[i]; + stftOutput[1][startIndex + i] = tmp_im[i]; } //stftOutput[frame] = fftResult; } diff --git a/src/test/java/org/apache/sysds/test/component/matrix/LibMatrixSTFTTest.java b/src/test/java/org/apache/sysds/test/component/matrix/LibMatrixSTFTTest.java index c9850a1b90e..b76e1ad0cf3 100644 --- a/src/test/java/org/apache/sysds/test/component/matrix/LibMatrixSTFTTest.java +++ b/src/test/java/org/apache/sysds/test/component/matrix/LibMatrixSTFTTest.java @@ -2,9 +2,10 @@ import org.apache.sysds.runtime.matrix.data.MatrixBlock; import org.junit.Test; -//import static org.apache.sysds.runtime.matrix.data.LibMatrixFourier.*; + import static org.junit.Assert.assertArrayEquals; -import static org.apache.sysds.runtime.matrix.data.LibMatrixSTFT.*; +import static org.apache.sysds.runtime.matrix.data.LibMatrixSTFT.stft; +import static org.apache.sysds.runtime.matrix.data.LibMatrixSTFT.one_dim_stft; public class LibMatrixSTFTTest { From af67b41127e882d1438ed36730b68afd62c491b7 Mon Sep 17 00:00:00 2001 From: Jessica Priebe Date: Wed, 17 Jan 2024 15:37:31 +0100 Subject: [PATCH 033/133] some styling --- .../runtime/matrix/data/LibCommonsMath.java | 7 ++- .../runtime/matrix/data/LibMatrixFourier.java | 4 +- .../runtime/matrix/data/LibMatrixSTFT.java | 18 +------- .../test/component/matrix/FourierTest.java | 5 ++- .../matrix/FourierTestWithFiles.java | 43 +++++++++++++------ .../component/matrix/LibMatrixSTFTTest.java | 33 +++----------- 6 files changed, 47 insertions(+), 63 deletions(-) diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java index 9297e184b7a..0b08979f92c 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java @@ -264,18 +264,17 @@ private static MatrixBlock[] computeFFT(MatrixBlock in) { throw new DMLRuntimeException("Invalid empty block"); //run fft + in.sparseToDense(); return fft(in); } private static MatrixBlock[] computeIFFT(MatrixBlock in) { if( in == null || in.isEmptyBlock(false)) throw new DMLRuntimeException("Invalid empty block"); - int rows = in.getNumRows(); - int cols = in.getNumColumns(); - MatrixBlock inIm = new MatrixBlock(rows, cols, new double[cols*rows]); //run ifft - return ifft(in, inIm); + in.sparseToDense(); + return ifft(in); } /** diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java index 61124a60b24..763e0940af1 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java @@ -1,7 +1,6 @@ package org.apache.sysds.runtime.matrix.data; import org.apache.commons.math3.util.FastMath; -import java.util.Arrays; public class LibMatrixFourier { @@ -58,6 +57,7 @@ public static void ifft(double[] re, double[] im, int rows, int cols) { for(int i = 0; i < rows; i++){ ifft_one_dim(re, im, re_inter, im_inter, i*cols, 1, cols, cols); } + } public static void fft_one_dim(double[] re, double[] im, double[] re_inter, double[] im_inter, int start, int step, int num, int subArraySize) { @@ -94,6 +94,7 @@ public static void fft_one_dim(double[] re, double[] im, double[] re_inter, doub re_inter[j] = 0; im_inter[j] = 0; } + } public static void ifft_one_dim(double[] re, double[] im, double[] re_inter, double[] im_inter, int start, int step, int num, int subArraySize) { @@ -111,6 +112,7 @@ public static void ifft_one_dim(double[] re, double[] im, double[] re_inter, dou re[i] = re[i]/num; im[i] = -im[i]/num; } + } private static boolean isPowerOfTwo(int n){ diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixSTFT.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixSTFT.java index d5fd490377e..9aa122e1a96 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixSTFT.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixSTFT.java @@ -1,24 +1,11 @@ package org.apache.sysds.runtime.matrix.data; -import java.util.Arrays; - import static org.apache.sysds.runtime.matrix.data.LibMatrixFourier.fft; public class LibMatrixSTFT { public static MatrixBlock[] stft(MatrixBlock re, MatrixBlock im, int windowSize, int overlap) { - /* - int dim = re.getNumRows(); - - double[][] in = new double[2][dim]; - in[0] = convertToArray(re); - //in[1] = convertToArray(im); - double[][] res = one_dim_stft(in[0], windowSize, overlap); - - return convertToMatrixBlocks(res); - */ - int rows = re.getNumRows(); int cols = re.getNumColumns(); double[][] in = new double[2][cols]; @@ -31,6 +18,7 @@ public static MatrixBlock[] stft(MatrixBlock re, MatrixBlock im, int windowSize, } public static double[][] one_dim_stft(double[] signal, int windowSize, int overlap) { + if (windowSize < 1 || (windowSize & (windowSize - 1)) != 0) { throw new IllegalArgumentException("frameSize is not a power of two"); } @@ -43,7 +31,6 @@ public static double[][] one_dim_stft(double[] signal, int windowSize, int overl for (int frame = 0; frame < totalFrames; frame++) { double[] windowedSignal = new double[windowSize]; - // Apply window function (rectangular in this case) for (int i = 0; i < windowSize; i++) { int signalIndex = frame * stepSize + i; @@ -75,10 +62,8 @@ public static double[][] one_dim_stft(double[] signal, int windowSize, int overl return stftOutput; } - private static MatrixBlock[] convertToMatrixBlocks(double[][] in){ - int cols = in[0].length; MatrixBlock re = new MatrixBlock(1, cols, in[0]); @@ -87,7 +72,6 @@ private static MatrixBlock[] convertToMatrixBlocks(double[][] in){ return new MatrixBlock[]{re, im}; } - private static double[] convertToArray(MatrixBlock in){ return in.getDenseBlockValues(); } diff --git a/src/test/java/org/apache/sysds/test/component/matrix/FourierTest.java b/src/test/java/org/apache/sysds/test/component/matrix/FourierTest.java index 8e894af2601..dbc6e02dcae 100644 --- a/src/test/java/org/apache/sysds/test/component/matrix/FourierTest.java +++ b/src/test/java/org/apache/sysds/test/component/matrix/FourierTest.java @@ -30,6 +30,7 @@ public void test_fft_one_dim() { assertArrayEquals(expected_re, re, 0.0001); assertArrayEquals(expected_im, im, 0.0001); + } @Test @@ -82,6 +83,7 @@ public void test_ifft_one_dim_matrixBlock_2() { assertArrayEquals(in_re, res[0].getDenseBlockValues(), 0.0001); assertArrayEquals(in_im, res[1].getDenseBlockValues(), 0.0001); + } @Test @@ -131,6 +133,7 @@ public void test_fft_two_dim_matrixBlock_row_1() { assertArrayEquals(expected_im, res[1].getDenseBlockValues(), 0.0001); } + @Test public void test_fft_two_dim_matrixBlock_row_2() { @@ -147,4 +150,4 @@ public void test_fft_two_dim_matrixBlock_row_2() { } -} \ No newline at end of file +} diff --git a/src/test/java/org/apache/sysds/test/component/matrix/FourierTestWithFiles.java b/src/test/java/org/apache/sysds/test/component/matrix/FourierTestWithFiles.java index ddc2b66ea83..884bdec91bd 100644 --- a/src/test/java/org/apache/sysds/test/component/matrix/FourierTestWithFiles.java +++ b/src/test/java/org/apache/sysds/test/component/matrix/FourierTestWithFiles.java @@ -1,15 +1,14 @@ package org.apache.sysds.test.component.matrix; -import org.apache.sysds.runtime.matrix.data.MatrixBlock; import org.junit.Test; +import java.io.IOException; +import java.io.BufferedReader; +import java.io.FileReader; -import java.io.*; - +import static org.junit.Assert.assertTrue; import static org.apache.sysds.runtime.matrix.data.LibMatrixFourier.fft; import static org.apache.sysds.runtime.matrix.data.LibMatrixFourier.ifft; -import static org.junit.Assert.assertTrue; - public class FourierTestWithFiles { int progressInterval = 5000; @@ -17,6 +16,7 @@ public class FourierTestWithFiles { // and add the generated files to the root of the project. @Test public void testFftWithNumpyData() throws IOException { + String filename = "fft_data.csv"; // Path to your CSV file String path = "./src/test/java/org/apache/sysds/test/component/matrix/"; @@ -64,17 +64,19 @@ public void testFftWithNumpyData() throws IOException { } reader.close(); - } private void validateFftResults(double[][] expected, double[][] actual, int lineNumber) { + int length = expected[0].length; + for (int i = 0; i < length; i++) { double realActual = actual[0][i]; double imagActual = actual[1][i]; assertEquals("Mismatch in real part at index " + i + " in line " + lineNumber, expected[0][i], realActual, 1e-9); assertEquals("Mismatch in imaginary part at index " + i + " in line " + lineNumber, expected[1][i], imagActual, 1e-9); } + if(lineNumber % progressInterval == 0){ System.out.println("fft(double[][][] in): Finished processing line " + lineNumber); } @@ -83,6 +85,7 @@ private void validateFftResults(double[][] expected, double[][] actual, int line @Test public void testFftExecutionTime() throws IOException { + String filename = "fft_data.csv"; // Path to your CSV file String path = "./src/test/java/org/apache/sysds/test/component/matrix/"; @@ -93,6 +96,7 @@ public void testFftExecutionTime() throws IOException { int numCalculations = 0; // Number of FFT computations while ((line = reader.readLine()) != null) { + lineNumber++; String[] values = line.split(","); int n = values.length / 3; @@ -110,6 +114,7 @@ public void testFftExecutionTime() throws IOException { fft(re, im, 1, n); long endTime = System.nanoTime(); + if(lineNumber > 1000){ totalTime += (endTime - startTime); numCalculations++; @@ -126,6 +131,7 @@ public void testFftExecutionTime() throws IOException { @Test public void testFftExecutionTimeOfOneDimFFT() throws IOException { + String filename = "fft_data.csv"; // Path to your CSV file String path = "./src/test/java/org/apache/sysds/test/component/matrix/"; @@ -136,6 +142,7 @@ public void testFftExecutionTimeOfOneDimFFT() throws IOException { int numCalculations = 0; // Number of FFT computations while ((line = reader.readLine()) != null) { + lineNumber++; String[] values = line.split(","); int n = values.length / 2; @@ -153,6 +160,7 @@ public void testFftExecutionTimeOfOneDimFFT() throws IOException { fft(re, im, 1, n); long endTime = System.nanoTime(); + if(lineNumber > 1000){ totalTime += (endTime - startTime); numCalculations++; @@ -167,10 +175,10 @@ public void testFftExecutionTimeOfOneDimFFT() throws IOException { reader.close(); } - // prior to executing this test it is necessary to run the Numpy Script in FourierTestData.py and add the generated file to the root of the project. @Test public void testIfftWithRealNumpyData() throws IOException { + String filename = "ifft_data.csv"; // Path to your CSV file String path = "./src/test/java/org/apache/sysds/test/component/matrix/"; @@ -179,6 +187,7 @@ public void testIfftWithRealNumpyData() throws IOException { int lineNumber = 0; while ((line = reader.readLine()) != null) { + lineNumber++; String[] values = line.split(","); int n = values.length / 3; @@ -203,11 +212,12 @@ public void testIfftWithRealNumpyData() throws IOException { } reader.close(); - } + } @Test public void testIfftWithComplexNumpyData() throws IOException { + String filename = "complex_ifft_data.csv"; // Adjusted path to your IFFT data file with complex inputs String path = "./src/test/java/org/apache/sysds/test/component/matrix/"; @@ -260,21 +270,25 @@ public void testIfftWithComplexNumpyData() throws IOException { } private void validateComplexIFftResults(double[][] expected, double[][] actual, int lineNumber) { + int length = expected[0].length; + for (int i = 0; i < length; i++) { double realActual = actual[0][i]; double imagActual = actual[1][i]; assertEquals("Mismatch in real part at index " + i + " in line " + lineNumber, expected[0][i], realActual, 1e-9); assertEquals("Mismatch in imaginary part at index " + i + " in line " + lineNumber, expected[1][i], imagActual, 1e-9); } + if (lineNumber % progressInterval == 0) { - System.out.println("ifft(complex input): Finished processing line " + lineNumber); + System.out.println("ifft(complex input): Finished processing line " + lineNumber); } - } + } @Test public void testFft2dWithNumpyData() throws IOException { + String filename = "complex_fft_2d_data.csv"; // path to your 2D FFT data file String path = "./src/test/java/org/apache/sysds/test/component/matrix/"; @@ -331,11 +345,13 @@ public void testFft2dWithNumpyData() throws IOException { reader.close(); System.out.println("fft2d: Finished processing " + lineNumber + " lines.\n Average execution time: " + String.format("%.8f", (totalTime / 1e6 / numCalculations)/1000) + " s"); + } @Test public void testIfft2dWithNumpyData() throws IOException { + String filename = "complex_ifft_2d_data.csv"; // path to your 2D IFFT data file String path = "./src/test/java/org/apache/sysds/test/component/matrix/"; @@ -346,6 +362,7 @@ public void testIfft2dWithNumpyData() throws IOException { int numCalculations = 0; // Number of IFFT 2D computations while ((line = reader.readLine()) != null) { + lineNumber++; String[] values = line.split(","); int halfLength = values.length / 4; @@ -396,6 +413,7 @@ public void testIfft2dWithNumpyData() throws IOException { reader.close(); System.out.println("ifft2d: Finished processing " + lineNumber + " lines.\n Average execution time: " + String.format("%.8f", (totalTime / 1e6 / numCalculations)/1000) + " ms"); + } // Helper method for asserting equality with a tolerance @@ -404,12 +422,13 @@ private static void assertEquals(String message, double expected, double actual, } private void assertComplexEquals(String message, double expectedReal, double expectedImag, double actualReal, double actualImag) { + final double EPSILON = 1e-9; assertTrue(message + " - Mismatch in real part. Expected: " + expectedReal + ", Actual: " + actualReal, Math.abs(expectedReal - actualReal) <= EPSILON); assertTrue(message + " - Mismatch in imaginary part. Expected: " + expectedImag + ", Actual: " + actualImag, Math.abs(expectedImag - actualImag) <= EPSILON); + } - -} \ No newline at end of file +} diff --git a/src/test/java/org/apache/sysds/test/component/matrix/LibMatrixSTFTTest.java b/src/test/java/org/apache/sysds/test/component/matrix/LibMatrixSTFTTest.java index b76e1ad0cf3..34cb47d0f65 100644 --- a/src/test/java/org/apache/sysds/test/component/matrix/LibMatrixSTFTTest.java +++ b/src/test/java/org/apache/sysds/test/component/matrix/LibMatrixSTFTTest.java @@ -7,7 +7,6 @@ import static org.apache.sysds.runtime.matrix.data.LibMatrixSTFT.stft; import static org.apache.sysds.runtime.matrix.data.LibMatrixSTFT.one_dim_stft; - public class LibMatrixSTFTTest { @Test @@ -27,8 +26,10 @@ public void simple_test() { } System.out.println(); } + assertArrayEquals(expected[0], stftResult[0], 0.0001); assertArrayEquals(expected[1], stftResult[1], 0.0001); + } @Test @@ -53,6 +54,7 @@ public void matrix_block_one_dim_test(){ assertArrayEquals(expected_re, res_re, 0.0001); assertArrayEquals(expected_im, res_im, 0.0001); + } @Test @@ -77,32 +79,7 @@ public void matrix_block_one_dim_test2(){ assertArrayEquals(expected_re, res_re, 0.0001); assertArrayEquals(expected_im, res_im, 0.0001); - } - - /* - public static void main(String[] args) { - - // Generate the sinusoidal signal - //double[] signal = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}; - double[] signal = {10, 5, -3, 8, 15, -6, 2, 0}; - - - // Define STFT parameters - int frameSize = 4; - int overlap = 2; - - // Perform the STFT - double[][] stftResult = one_dim_stft(signal, frameSize, overlap); - - // tensorflow change arguments names it is calles step - // Output some results for verification - // also for 2d array - System.out.println("STFT Result (a few samples):"); - int l = stftResult[0].length; - for (int i = 0; i < l; i++) { - System.out.println("Real = " + stftResult[0][i] + ", Imaginary = " + stftResult[1][i]); - } } - */ -} \ No newline at end of file + +} From 759083434ad798a09be1efc564d5418f7aebd6ab Mon Sep 17 00:00:00 2001 From: Jessica Priebe Date: Wed, 17 Jan 2024 16:00:28 +0100 Subject: [PATCH 034/133] switch cases --- pom.xml | 4 +- .../runtime/matrix/data/LibCommonsMath.java | 45 ++++++++++--------- 2 files changed, 27 insertions(+), 22 deletions(-) diff --git a/pom.xml b/pom.xml index 4cacb56fb37..0d3b5df4ae7 100644 --- a/pom.xml +++ b/pom.xml @@ -322,8 +322,8 @@ ${maven-compiler-plugin.version} -Xlint:unchecked - ${java.level} - ${java.level} + 14 + 14 ${java.level} diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java index 0b08979f92c..a6bbc1ef6b3 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java @@ -74,7 +74,17 @@ public static boolean isSupportedUnaryOperation( String opcode ) { } public static boolean isSupportedMultiReturnOperation( String opcode ) { - return ( opcode.equals("qr") || opcode.equals("lu") || opcode.equals("eigen") || opcode.equals("fft") || opcode.equals("ifft") || opcode.equals("svd") ); + + switch (opcode) { + case "qr": + case "lu": + case "eigen": + case "fft": + case "ifft": + case "svd": return true; + default: return false; + } + } public static boolean isSupportedMatrixMatrixOperation( String opcode ) { @@ -102,25 +112,20 @@ public static MatrixBlock[] multiReturnOperations(MatrixBlock in, String opcode, } public static MatrixBlock[] multiReturnOperations(MatrixBlock in, String opcode, int threads, long seed) { - if(opcode.equals("qr")) - return computeQR(in); - else if (opcode.equals("qr2")) - return computeQR2(in, threads); - else if (opcode.equals("lu")) - return computeLU(in); - else if (opcode.equals("eigen")) - return computeEigen(in); - else if (opcode.equals("eigen_lanczos")) - return computeEigenLanczos(in, threads, seed); - else if (opcode.equals("eigen_qr")) - return computeEigenQR(in, threads); - else if (opcode.equals("fft")) - return computeFFT(in); - else if (opcode.equals("ifft")) - return computeIFFT(in); - else if (opcode.equals("svd")) - return computeSvd(in); - return null; + + switch (opcode) { + case "qr": return computeQR(in); + case "qr2": return computeQR2(in, threads); + case "lu": return computeLU(in); + case "eigen": return computeEigen(in); + case "eigen_lanczos": return computeEigenLanczos(in, threads, seed); + case "eigen_qr": return computeEigenQR(in, threads); + case "fft": return computeFFT(in); + case "ifft": return computeIFFT(in); + case "svd": return computeSvd(in); + default: return null; + } + } public static MatrixBlock matrixMatrixOperations(MatrixBlock in1, MatrixBlock in2, String opcode) { From 465d3e40e63536810a98ff841bc0e443c25fc82a Mon Sep 17 00:00:00 2001 From: Jessica Priebe Date: Wed, 17 Jan 2024 16:03:42 +0100 Subject: [PATCH 035/133] undo recent change in pom.xml --- pom.xml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pom.xml b/pom.xml index 0d3b5df4ae7..4cacb56fb37 100644 --- a/pom.xml +++ b/pom.xml @@ -322,8 +322,8 @@ ${maven-compiler-plugin.version} -Xlint:unchecked - 14 - 14 + ${java.level} + ${java.level} ${java.level} From d11e5984023ae7e95f6def844b0d7b46145f571b Mon Sep 17 00:00:00 2001 From: Jessica Priebe Date: Wed, 17 Jan 2024 16:22:38 +0100 Subject: [PATCH 036/133] added licence --- .../runtime/matrix/data/LibMatrixFourier.java | 19 +++++++++++++++++++ .../runtime/matrix/data/LibMatrixSTFT.java | 19 +++++++++++++++++++ .../test/component/matrix/FourierTest.java | 19 +++++++++++++++++++ .../test/component/matrix/FourierTestData.py | 19 +++++++++++++++++++ .../matrix/FourierTestWithFiles.java | 19 +++++++++++++++++++ .../component/matrix/LibMatrixSTFTTest.java | 19 +++++++++++++++++++ 6 files changed, 114 insertions(+) diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java index 763e0940af1..53678fdf064 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java @@ -1,3 +1,22 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + package org.apache.sysds.runtime.matrix.data; import org.apache.commons.math3.util.FastMath; diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixSTFT.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixSTFT.java index 9aa122e1a96..9cad41d5245 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixSTFT.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixSTFT.java @@ -1,3 +1,22 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + package org.apache.sysds.runtime.matrix.data; import static org.apache.sysds.runtime.matrix.data.LibMatrixFourier.fft; diff --git a/src/test/java/org/apache/sysds/test/component/matrix/FourierTest.java b/src/test/java/org/apache/sysds/test/component/matrix/FourierTest.java index dbc6e02dcae..2866ee40eb4 100644 --- a/src/test/java/org/apache/sysds/test/component/matrix/FourierTest.java +++ b/src/test/java/org/apache/sysds/test/component/matrix/FourierTest.java @@ -1,3 +1,22 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + package org.apache.sysds.test.component.matrix; import org.apache.sysds.runtime.matrix.data.MatrixBlock; diff --git a/src/test/java/org/apache/sysds/test/component/matrix/FourierTestData.py b/src/test/java/org/apache/sysds/test/component/matrix/FourierTestData.py index 9b7aaa0233f..fd01e69d893 100644 --- a/src/test/java/org/apache/sysds/test/component/matrix/FourierTestData.py +++ b/src/test/java/org/apache/sysds/test/component/matrix/FourierTestData.py @@ -1,3 +1,22 @@ +""" +Licensed to the Apache Software Foundation (ASF) under one +or more contributor license agreements. See the NOTICE file +distributed with this work for additional information +regarding copyright ownership. The ASF licenses this file +to you under the Apache License, Version 2.0 (the +"License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, +software distributed under the License is distributed on an +"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, either express or implied. See the License for the +specific language governing permissions and limitations +under the License. +""" + import numpy as np import csv import time diff --git a/src/test/java/org/apache/sysds/test/component/matrix/FourierTestWithFiles.java b/src/test/java/org/apache/sysds/test/component/matrix/FourierTestWithFiles.java index 884bdec91bd..acf8738c60c 100644 --- a/src/test/java/org/apache/sysds/test/component/matrix/FourierTestWithFiles.java +++ b/src/test/java/org/apache/sysds/test/component/matrix/FourierTestWithFiles.java @@ -1,3 +1,22 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + package org.apache.sysds.test.component.matrix; import org.junit.Test; diff --git a/src/test/java/org/apache/sysds/test/component/matrix/LibMatrixSTFTTest.java b/src/test/java/org/apache/sysds/test/component/matrix/LibMatrixSTFTTest.java index 34cb47d0f65..9377c1d3a28 100644 --- a/src/test/java/org/apache/sysds/test/component/matrix/LibMatrixSTFTTest.java +++ b/src/test/java/org/apache/sysds/test/component/matrix/LibMatrixSTFTTest.java @@ -1,3 +1,22 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + package org.apache.sysds.test.component.matrix; import org.apache.sysds.runtime.matrix.data.MatrixBlock; From 3eb8677e31cc56381956628f0248b4c327acb97f Mon Sep 17 00:00:00 2001 From: Jessica Priebe Date: Wed, 17 Jan 2024 16:46:26 +0100 Subject: [PATCH 037/133] removed json --- pom.xml | 6 ------ 1 file changed, 6 deletions(-) diff --git a/pom.xml b/pom.xml index 4cacb56fb37..b97cfb30ada 100644 --- a/pom.xml +++ b/pom.xml @@ -875,12 +875,6 @@ - - org.json - json - 20210307 - - org.jcuda jcuda From f21b4ed9fae5b858f36c7668893aead8df2a4329 Mon Sep 17 00:00:00 2001 From: Jessica Priebe Date: Thu, 18 Jan 2024 13:57:19 +0100 Subject: [PATCH 038/133] tab indentation --- .../runtime/matrix/data/LibMatrixFourier.java | 172 ++--- .../runtime/matrix/data/LibMatrixSTFT.java | 124 ++-- .../test/component/matrix/FourierTest.java | 174 ++--- .../test/component/matrix/FourierTestData.py | 222 +++--- .../matrix/FourierTestWithFiles.java | 696 +++++++++--------- .../component/matrix/LibMatrixSTFTTest.java | 106 +-- 6 files changed, 747 insertions(+), 747 deletions(-) diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java index 53678fdf064..e8283254191 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java @@ -23,127 +23,127 @@ public class LibMatrixFourier { - /** - * Function to perform Fast Fourier Transformation - */ + /** + * Function to perform Fast Fourier Transformation + */ - public static MatrixBlock[] fft(MatrixBlock re, MatrixBlock im){ + public static MatrixBlock[] fft(MatrixBlock re, MatrixBlock im){ - int rows = re.getNumRows(); - int cols = re.getNumColumns(); - if(!isPowerOfTwo(rows) || !isPowerOfTwo(cols)) throw new RuntimeException("false dimensions"); + int rows = re.getNumRows(); + int cols = re.getNumColumns(); + if(!isPowerOfTwo(rows) || !isPowerOfTwo(cols)) throw new RuntimeException("false dimensions"); - fft(re.getDenseBlockValues(), im.getDenseBlockValues(), rows, cols); + fft(re.getDenseBlockValues(), im.getDenseBlockValues(), rows, cols); - return new MatrixBlock[]{re, im}; - } + return new MatrixBlock[]{re, im}; + } - public static MatrixBlock[] ifft(MatrixBlock re, MatrixBlock im){ + public static MatrixBlock[] ifft(MatrixBlock re, MatrixBlock im){ - int rows = re.getNumRows(); - int cols = re.getNumColumns(); - if(!isPowerOfTwo(rows) || !isPowerOfTwo(cols)) throw new RuntimeException("false dimensions"); + int rows = re.getNumRows(); + int cols = re.getNumColumns(); + if(!isPowerOfTwo(rows) || !isPowerOfTwo(cols)) throw new RuntimeException("false dimensions"); - ifft(re.getDenseBlockValues(), im.getDenseBlockValues(), rows, cols); + ifft(re.getDenseBlockValues(), im.getDenseBlockValues(), rows, cols); - return new MatrixBlock[]{re, im}; - } + return new MatrixBlock[]{re, im}; + } - public static void fft(double[] re, double[] im, int rows, int cols) { + public static void fft(double[] re, double[] im, int rows, int cols) { - double[] re_inter = new double[rows*cols]; - double[] im_inter = new double[rows*cols]; + double[] re_inter = new double[rows*cols]; + double[] im_inter = new double[rows*cols]; - for(int i = 0; i < rows; i++){ - fft_one_dim(re, im, re_inter, im_inter, i*cols, 1, cols, cols); - } + for(int i = 0; i < rows; i++){ + fft_one_dim(re, im, re_inter, im_inter, i*cols, 1, cols, cols); + } - for(int j = 0; j < cols; j++){ - fft_one_dim(re, im, re_inter, im_inter, j, cols, rows, rows*cols); - } + for(int j = 0; j < cols; j++){ + fft_one_dim(re, im, re_inter, im_inter, j, cols, rows, rows*cols); + } - } + } - public static void ifft(double[] re, double[] im, int rows, int cols) { + public static void ifft(double[] re, double[] im, int rows, int cols) { - double[] re_inter = new double[rows*cols]; - double[] im_inter = new double[rows*cols]; + double[] re_inter = new double[rows*cols]; + double[] im_inter = new double[rows*cols]; - for(int j = 0; j < cols; j++){ - ifft_one_dim(re, im, re_inter, im_inter, j, cols, rows, rows*cols); - } + for(int j = 0; j < cols; j++){ + ifft_one_dim(re, im, re_inter, im_inter, j, cols, rows, rows*cols); + } - for(int i = 0; i < rows; i++){ - ifft_one_dim(re, im, re_inter, im_inter, i*cols, 1, cols, cols); - } + for(int i = 0; i < rows; i++){ + ifft_one_dim(re, im, re_inter, im_inter, i*cols, 1, cols, cols); + } - } + } - public static void fft_one_dim(double[] re, double[] im, double[] re_inter, double[] im_inter, int start, int step, int num, int subArraySize) { + public static void fft_one_dim(double[] re, double[] im, double[] re_inter, double[] im_inter, int start, int step, int num, int subArraySize) { - if(num == 1) return; - double angle = -2*FastMath.PI/num; + if(num == 1) return; + double angle = -2*FastMath.PI/num; - // fft for even indices - fft_one_dim(re, im, re_inter, im_inter, start, step*2, num/2, subArraySize); - // fft for odd indices - fft_one_dim(re, im, re_inter, im_inter,start+step, step*2, num/2, subArraySize); + // fft for even indices + fft_one_dim(re, im, re_inter, im_inter, start, step*2, num/2, subArraySize); + // fft for odd indices + fft_one_dim(re, im, re_inter, im_inter,start+step, step*2, num/2, subArraySize); - // iterates over even indices - for(int j = start, cnt = 0; cnt < num/2; j+=(2*step), cnt++){ + // iterates over even indices + for(int j = start, cnt = 0; cnt < num/2; j+=(2*step), cnt++){ - double omega_pow_re = FastMath.cos(cnt*angle); - double omega_pow_im = FastMath.sin(cnt*angle); + double omega_pow_re = FastMath.cos(cnt*angle); + double omega_pow_im = FastMath.sin(cnt*angle); - // calculate m using the result of odd index - double m_re = omega_pow_re * re[j+step] - omega_pow_im * im[j+step]; - double m_im = omega_pow_re * im[j+step] + omega_pow_im * re[j+step]; + // calculate m using the result of odd index + double m_re = omega_pow_re * re[j+step] - omega_pow_im * im[j+step]; + double m_im = omega_pow_re * im[j+step] + omega_pow_im * re[j+step]; - int index = start+cnt*step; - re_inter[index] = re[j] + m_re; - re_inter[index+subArraySize/2] = re[j] - m_re; + int index = start+cnt*step; + re_inter[index] = re[j] + m_re; + re_inter[index+subArraySize/2] = re[j] - m_re; - im_inter[index] = im[j] + m_im; - im_inter[index+subArraySize/2] = im[j] - m_im; - } + im_inter[index] = im[j] + m_im; + im_inter[index+subArraySize/2] = im[j] - m_im; + } - for(int j = start; j < start+subArraySize; j+=step){ - re[j] = re_inter[j]; - im[j] = im_inter[j]; - re_inter[j] = 0; - im_inter[j] = 0; - } + for(int j = start; j < start+subArraySize; j+=step){ + re[j] = re_inter[j]; + im[j] = im_inter[j]; + re_inter[j] = 0; + im_inter[j] = 0; + } - } + } - public static void ifft_one_dim(double[] re, double[] im, double[] re_inter, double[] im_inter, int start, int step, int num, int subArraySize) { + public static void ifft_one_dim(double[] re, double[] im, double[] re_inter, double[] im_inter, int start, int step, int num, int subArraySize) { - // conjugate input - for (int i = start; i < start+num*step; i+=step){ - im[i] = -im[i]; - } + // conjugate input + for (int i = start; i < start+num*step; i+=step){ + im[i] = -im[i]; + } - // apply fft - fft_one_dim(re, im, re_inter, im_inter, start, step, num, subArraySize); + // apply fft + fft_one_dim(re, im, re_inter, im_inter, start, step, num, subArraySize); - // conjugate and scale result - for (int i = start; i < start+num*step; i+=step){ - re[i] = re[i]/num; - im[i] = -im[i]/num; - } + // conjugate and scale result + for (int i = start; i < start+num*step; i+=step){ + re[i] = re[i]/num; + im[i] = -im[i]/num; + } - } + } - private static boolean isPowerOfTwo(int n){ - return (n != 0) && ((n & (n - 1)) == 0); - } + private static boolean isPowerOfTwo(int n){ + return (n != 0) && ((n & (n - 1)) == 0); + } - public static MatrixBlock[] fft(MatrixBlock re){ - return fft(re, new MatrixBlock(re.getNumRows(),re.getNumColumns(), new double[re.getNumRows()*re.getNumColumns()])); - } + public static MatrixBlock[] fft(MatrixBlock re){ + return fft(re, new MatrixBlock(re.getNumRows(),re.getNumColumns(), new double[re.getNumRows()*re.getNumColumns()])); + } - public static MatrixBlock[] ifft(MatrixBlock re){ - return ifft(re, new MatrixBlock(re.getNumRows(),re.getNumColumns(), new double[re.getNumRows()*re.getNumColumns()])); - } + public static MatrixBlock[] ifft(MatrixBlock re){ + return ifft(re, new MatrixBlock(re.getNumRows(),re.getNumColumns(), new double[re.getNumRows()*re.getNumColumns()])); + } } diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixSTFT.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixSTFT.java index 9cad41d5245..01c61e5746f 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixSTFT.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixSTFT.java @@ -23,90 +23,90 @@ public class LibMatrixSTFT { - public static MatrixBlock[] stft(MatrixBlock re, MatrixBlock im, int windowSize, int overlap) { + public static MatrixBlock[] stft(MatrixBlock re, MatrixBlock im, int windowSize, int overlap) { - int cols = re.getNumColumns(); + int cols = re.getNumColumns(); - double[][] in = new double[2][cols]; - in[0] = convertToArray(re); - in[1] = convertToArray(im); + double[][] in = new double[2][cols]; + in[0] = convertToArray(re); + in[1] = convertToArray(im); - double[][] res = one_dim_stft(in[0], windowSize, overlap); + double[][] res = one_dim_stft(in[0], windowSize, overlap); - return convertToMatrixBlocks(res); - } + return convertToMatrixBlocks(res); + } - public static double[][] one_dim_stft(double[] signal, int windowSize, int overlap) { + public static double[][] one_dim_stft(double[] signal, int windowSize, int overlap) { - if (windowSize < 1 || (windowSize & (windowSize - 1)) != 0) { - throw new IllegalArgumentException("frameSize is not a power of two"); - } - int stepSize = windowSize - overlap; - int totalFrames = (signal.length - overlap) / stepSize; + if (windowSize < 1 || (windowSize & (windowSize - 1)) != 0) { + throw new IllegalArgumentException("frameSize is not a power of two"); + } + int stepSize = windowSize - overlap; + int totalFrames = (signal.length - overlap) / stepSize; - // Initialize the STFT output array: [time frame][frequency bin][real & imaginary parts] - double[][] stftOutput = new double[2][totalFrames * windowSize]; + // Initialize the STFT output array: [time frame][frequency bin][real & imaginary parts] + double[][] stftOutput = new double[2][totalFrames * windowSize]; - for (int frame = 0; frame < totalFrames; frame++) { - double[] windowedSignal = new double[windowSize]; + for (int frame = 0; frame < totalFrames; frame++) { + double[] windowedSignal = new double[windowSize]; - // Apply window function (rectangular in this case) - for (int i = 0; i < windowSize; i++) { - int signalIndex = frame * stepSize + i; - windowedSignal[i] = (signalIndex < signal.length) ? signal[signalIndex] : 0; - } + // Apply window function (rectangular in this case) + for (int i = 0; i < windowSize; i++) { + int signalIndex = frame * stepSize + i; + windowedSignal[i] = (signalIndex < signal.length) ? signal[signalIndex] : 0; + } - // Perform FFT on windowed signal - int l = windowedSignal.length; + // Perform FFT on windowed signal + int l = windowedSignal.length; - double[] tmp_re = new double[l]; - double[] tmp_im = new double[l]; + double[] tmp_re = new double[l]; + double[] tmp_im = new double[l]; - for (int i = 0; i < l; i++) { - tmp_re[i] = windowedSignal[i]; - tmp_im[i] = 0; - } + for (int i = 0; i < l; i++) { + tmp_re[i] = windowedSignal[i]; + tmp_im[i] = 0; + } - fft(tmp_re, tmp_im, 1, l); + fft(tmp_re, tmp_im, 1, l); - // Store the FFT result in the output array - int startIndex = windowSize * frame; - for (int i = 0; i < l; i++) { - stftOutput[0][startIndex + i] = tmp_re[i]; - stftOutput[1][startIndex + i] = tmp_im[i]; - } - //stftOutput[frame] = fftResult; - } + // Store the FFT result in the output array + int startIndex = windowSize * frame; + for (int i = 0; i < l; i++) { + stftOutput[0][startIndex + i] = tmp_re[i]; + stftOutput[1][startIndex + i] = tmp_im[i]; + } + //stftOutput[frame] = fftResult; + } - return stftOutput; - } + return stftOutput; + } - private static MatrixBlock[] convertToMatrixBlocks(double[][] in){ + private static MatrixBlock[] convertToMatrixBlocks(double[][] in){ - int cols = in[0].length; + int cols = in[0].length; - MatrixBlock re = new MatrixBlock(1, cols, in[0]); - MatrixBlock im = new MatrixBlock(1, cols, in[1]); + MatrixBlock re = new MatrixBlock(1, cols, in[0]); + MatrixBlock im = new MatrixBlock(1, cols, in[1]); - return new MatrixBlock[]{re, im}; - } + return new MatrixBlock[]{re, im}; + } - private static double[] convertToArray(MatrixBlock in){ - return in.getDenseBlockValues(); - } + private static double[] convertToArray(MatrixBlock in){ + return in.getDenseBlockValues(); + } - public static MatrixBlock[] stft(double[] in, int windowSize, int overlap){ - double[][] arr = new double[2][in.length]; - arr[0] = in; - return stft(convertToMatrixBlocks(arr), windowSize, overlap); - } + public static MatrixBlock[] stft(double[] in, int windowSize, int overlap){ + double[][] arr = new double[2][in.length]; + arr[0] = in; + return stft(convertToMatrixBlocks(arr), windowSize, overlap); + } - public static MatrixBlock[] stft(double[][] in, int windowSize, int overlap){ - return stft(convertToMatrixBlocks(in), windowSize, overlap); - } + public static MatrixBlock[] stft(double[][] in, int windowSize, int overlap){ + return stft(convertToMatrixBlocks(in), windowSize, overlap); + } - public static MatrixBlock[] stft(MatrixBlock[] in, int windowSize, int overlap){ - return stft(in[0], in[1], windowSize, overlap); - } + public static MatrixBlock[] stft(MatrixBlock[] in, int windowSize, int overlap){ + return stft(in[0], in[1], windowSize, overlap); + } } diff --git a/src/test/java/org/apache/sysds/test/component/matrix/FourierTest.java b/src/test/java/org/apache/sysds/test/component/matrix/FourierTest.java index 2866ee40eb4..41a8e48b571 100644 --- a/src/test/java/org/apache/sysds/test/component/matrix/FourierTest.java +++ b/src/test/java/org/apache/sysds/test/component/matrix/FourierTest.java @@ -31,142 +31,142 @@ public class FourierTest { - @Test - public void test_fft_one_dim() { + @Test + public void test_fft_one_dim() { - double[] re = {0, 18, -15, 3}; - double[] im = {0, 0, 0, 0}; + double[] re = {0, 18, -15, 3}; + double[] im = {0, 0, 0, 0}; - double[] re_inter = new double[4]; - double[] im_inter = new double[4]; + double[] re_inter = new double[4]; + double[] im_inter = new double[4]; - double[] expected_re = {6, 15, -36, 15}; - double[] expected_im = {0, -15, 0, 15}; + double[] expected_re = {6, 15, -36, 15}; + double[] expected_im = {0, -15, 0, 15}; - int cols = 4; + int cols = 4; - fft_one_dim(re, im, re_inter, im_inter, 0, 1, cols, cols); + fft_one_dim(re, im, re_inter, im_inter, 0, 1, cols, cols); - assertArrayEquals(expected_re, re, 0.0001); - assertArrayEquals(expected_im, im, 0.0001); + assertArrayEquals(expected_re, re, 0.0001); + assertArrayEquals(expected_im, im, 0.0001); - } + } - @Test - public void test_fft_one_dim_2() { + @Test + public void test_fft_one_dim_2() { - double[] re = {0, 18, -15, 3, 5, 10, 5, 9}; - double[] im = new double[8]; + double[] re = {0, 18, -15, 3, 5, 10, 5, 9}; + double[] im = new double[8]; - double[] re_inter = new double[8]; - double[] im_inter = new double[8]; + double[] re_inter = new double[8]; + double[] im_inter = new double[8]; - double[] expected_re = {35, 4.89949, 15, -14.89949, -45, -14.89949, 15, 4.89949}; - double[] expected_im = {0, 18.58579, -16, -21.41421, 0, 21.41421, 16, -18.58579}; + double[] expected_re = {35, 4.89949, 15, -14.89949, -45, -14.89949, 15, 4.89949}; + double[] expected_im = {0, 18.58579, -16, -21.41421, 0, 21.41421, 16, -18.58579}; - int cols = 8; + int cols = 8; - fft_one_dim(re, im, re_inter, im_inter, 0, 1, cols, cols); + fft_one_dim(re, im, re_inter, im_inter, 0, 1, cols, cols); - assertArrayEquals(expected_re, re, 0.0001); - assertArrayEquals(expected_im, im, 0.0001); - } + assertArrayEquals(expected_re, re, 0.0001); + assertArrayEquals(expected_im, im, 0.0001); + } - @Test - public void test_fft_one_dim_matrixBlock() { + @Test + public void test_fft_one_dim_matrixBlock() { - MatrixBlock re = new MatrixBlock(1, 4, new double[]{0, 18, -15, 3}); - MatrixBlock im = new MatrixBlock(1, 4, new double[]{0, 0, 0, 0}); + MatrixBlock re = new MatrixBlock(1, 4, new double[]{0, 18, -15, 3}); + MatrixBlock im = new MatrixBlock(1, 4, new double[]{0, 0, 0, 0}); - double[] expected_re = {6, 15, -36, 15}; - double[] expected_im = {0, -15, 0, 15}; + double[] expected_re = {6, 15, -36, 15}; + double[] expected_im = {0, -15, 0, 15}; - MatrixBlock[] res = fft(re, im); + MatrixBlock[] res = fft(re, im); - assertArrayEquals(expected_re, res[0].getDenseBlockValues(), 0.0001); - assertArrayEquals(expected_im, res[1].getDenseBlockValues(), 0.0001); + assertArrayEquals(expected_re, res[0].getDenseBlockValues(), 0.0001); + assertArrayEquals(expected_im, res[1].getDenseBlockValues(), 0.0001); - } + } - @Test - public void test_ifft_one_dim_matrixBlock_2() { + @Test + public void test_ifft_one_dim_matrixBlock_2() { - double[] in_re = new double[]{1, -2, 3, -4}; - double[] in_im = new double[]{0, 0, 0, 0}; + double[] in_re = new double[]{1, -2, 3, -4}; + double[] in_im = new double[]{0, 0, 0, 0}; - MatrixBlock re = new MatrixBlock(1, 4, in_re); - MatrixBlock im = new MatrixBlock(1, 4, in_im); + MatrixBlock re = new MatrixBlock(1, 4, in_re); + MatrixBlock im = new MatrixBlock(1, 4, in_im); - MatrixBlock[] inter = fft(re, im); - MatrixBlock[] res = ifft(inter[0], inter[1]); + MatrixBlock[] inter = fft(re, im); + MatrixBlock[] res = ifft(inter[0], inter[1]); - assertArrayEquals(in_re, res[0].getDenseBlockValues(), 0.0001); - assertArrayEquals(in_im, res[1].getDenseBlockValues(), 0.0001); + assertArrayEquals(in_re, res[0].getDenseBlockValues(), 0.0001); + assertArrayEquals(in_im, res[1].getDenseBlockValues(), 0.0001); - } + } - @Test - public void test_fft_two_dim_matrixBlock() { + @Test + public void test_fft_two_dim_matrixBlock() { - MatrixBlock re = new MatrixBlock(2, 2, new double[]{0, 18, -15, 3}); - MatrixBlock im = new MatrixBlock(2, 2, new double[]{0, 0, 0, 0}); + MatrixBlock re = new MatrixBlock(2, 2, new double[]{0, 18, -15, 3}); + MatrixBlock im = new MatrixBlock(2, 2, new double[]{0, 0, 0, 0}); - double[] expected_re = {6,-36, 30, 0}; - double[] expected_im = {0, 0, 0, 0}; + double[] expected_re = {6,-36, 30, 0}; + double[] expected_im = {0, 0, 0, 0}; - MatrixBlock[] res = fft(re, im); + MatrixBlock[] res = fft(re, im); - assertArrayEquals(expected_re, res[0].getDenseBlockValues(), 0.0001); - assertArrayEquals(expected_im, res[1].getDenseBlockValues(), 0.0001); + assertArrayEquals(expected_re, res[0].getDenseBlockValues(), 0.0001); + assertArrayEquals(expected_im, res[1].getDenseBlockValues(), 0.0001); - } + } - @Test - public void test_ifft_two_dim_matrixBlock() { + @Test + public void test_ifft_two_dim_matrixBlock() { - MatrixBlock re = new MatrixBlock(2, 2, new double[]{6,-36, 30, 0}); - MatrixBlock im = new MatrixBlock(2, 2, new double[]{0, 0, 0, 0}); + MatrixBlock re = new MatrixBlock(2, 2, new double[]{6,-36, 30, 0}); + MatrixBlock im = new MatrixBlock(2, 2, new double[]{0, 0, 0, 0}); - double[] expected_re = {0, 18, -15, 3}; - double[] expected_im = {0, 0, 0, 0}; + double[] expected_re = {0, 18, -15, 3}; + double[] expected_im = {0, 0, 0, 0}; - MatrixBlock[] res = ifft(re, im); + MatrixBlock[] res = ifft(re, im); - assertArrayEquals(expected_re, res[0].getDenseBlockValues(), 0.0001); - assertArrayEquals(expected_im, res[1].getDenseBlockValues(), 0.0001); + assertArrayEquals(expected_re, res[0].getDenseBlockValues(), 0.0001); + assertArrayEquals(expected_im, res[1].getDenseBlockValues(), 0.0001); - } + } - @Test - public void test_fft_two_dim_matrixBlock_row_1() { + @Test + public void test_fft_two_dim_matrixBlock_row_1() { - MatrixBlock re = new MatrixBlock(1, 2, new double[]{0, 18}); - MatrixBlock im = new MatrixBlock(1, 2, new double[]{0, 0}); + MatrixBlock re = new MatrixBlock(1, 2, new double[]{0, 18}); + MatrixBlock im = new MatrixBlock(1, 2, new double[]{0, 0}); - double[] expected_re = {18, -18}; - double[] expected_im = {0, 0}; + double[] expected_re = {18, -18}; + double[] expected_im = {0, 0}; - MatrixBlock[] res = fft(re, im); + MatrixBlock[] res = fft(re, im); - assertArrayEquals(expected_re, res[0].getDenseBlockValues(), 0.0001); - assertArrayEquals(expected_im, res[1].getDenseBlockValues(), 0.0001); + assertArrayEquals(expected_re, res[0].getDenseBlockValues(), 0.0001); + assertArrayEquals(expected_im, res[1].getDenseBlockValues(), 0.0001); - } + } - @Test - public void test_fft_two_dim_matrixBlock_row_2() { + @Test + public void test_fft_two_dim_matrixBlock_row_2() { - MatrixBlock re = new MatrixBlock(1, 2, new double[]{ -15, 3}); - MatrixBlock im = new MatrixBlock(1, 2, new double[]{0, 0}); + MatrixBlock re = new MatrixBlock(1, 2, new double[]{ -15, 3}); + MatrixBlock im = new MatrixBlock(1, 2, new double[]{0, 0}); - double[] expected_re = {-12, -18}; - double[] expected_im = {0, 0}; + double[] expected_re = {-12, -18}; + double[] expected_im = {0, 0}; - MatrixBlock[] res = fft(re, im); + MatrixBlock[] res = fft(re, im); - assertArrayEquals(expected_re, res[0].getDenseBlockValues(), 0.0001); - assertArrayEquals(expected_im, res[1].getDenseBlockValues(), 0.0001); + assertArrayEquals(expected_re, res[0].getDenseBlockValues(), 0.0001); + assertArrayEquals(expected_im, res[1].getDenseBlockValues(), 0.0001); - } + } } diff --git a/src/test/java/org/apache/sysds/test/component/matrix/FourierTestData.py b/src/test/java/org/apache/sysds/test/component/matrix/FourierTestData.py index fd01e69d893..371b837f6bb 100644 --- a/src/test/java/org/apache/sysds/test/component/matrix/FourierTestData.py +++ b/src/test/java/org/apache/sysds/test/component/matrix/FourierTestData.py @@ -22,129 +22,129 @@ import time def generate_inputs(num_inputs, max_power): - for _ in range(num_inputs): - power = np.random.randint(1, max_power+1) - length = 2 ** power - yield np.random.rand(length) # generate array of random floats + for _ in range(num_inputs): + power = np.random.randint(1, max_power+1) + length = 2 ** power + yield np.random.rand(length) # generate array of random floats def generate_complex_inputs(num_inputs, max_power): - for _ in range(num_inputs): - power = np.random.randint(1, max_power+1) - length = 2 ** power - real_part = np.random.rand(length) - imag_part = np.random.rand(length) - complex_array = real_part + 1j * imag_part - yield complex_array - + for _ in range(num_inputs): + power = np.random.randint(1, max_power+1) + length = 2 ** power + real_part = np.random.rand(length) + imag_part = np.random.rand(length) + complex_array = real_part + 1j * imag_part + yield complex_array + def compute_fft(inputs): - total_time = 0 - num_calculations = 0 + total_time = 0 + num_calculations = 0 - for input_array in inputs: - start_time = time.time() - result = np.fft.fft(input_array) - end_time = time.time() + for input_array in inputs: + start_time = time.time() + result = np.fft.fft(input_array) + end_time = time.time() - total_time += end_time - start_time - num_calculations += 1 + total_time += end_time - start_time + num_calculations += 1 - if num_calculations % 1000 == 0: - average_time = total_time / num_calculations - print(f"Average execution time after {num_calculations} calculations: {average_time:.6f} seconds") + if num_calculations % 1000 == 0: + average_time = total_time / num_calculations + print(f"Average execution time after {num_calculations} calculations: {average_time:.6f} seconds") - yield result + yield result def compute_ifft(inputs): - total_time = 0 - num_calculations = 0 + total_time = 0 + num_calculations = 0 + + for input_array in inputs: + start_time = time.time() + result = np.fft.ifft(input_array) + end_time = time.time() - for input_array in inputs: - start_time = time.time() - result = np.fft.ifft(input_array) - end_time = time.time() + total_time += end_time - start_time + num_calculations += 1 - total_time += end_time - start_time - num_calculations += 1 + if num_calculations % 1000 == 0: + average_time = total_time / num_calculations + print(f"Average execution time after {num_calculations} calculations: {average_time:.6f} seconds") - if num_calculations % 1000 == 0: - average_time = total_time / num_calculations - print(f"Average execution time after {num_calculations} calculations: {average_time:.6f} seconds") + yield result - yield result - def save_to_file(inputs, outputs, filename, mode='a'): - with open(filename, mode, newline='') as file: - writer = csv.writer(file) - for input_array, output_array in zip(inputs, outputs): - flattened_data = np.concatenate((input_array, output_array.real, output_array.imag)) - writer.writerow(flattened_data) + with open(filename, mode, newline='') as file: + writer = csv.writer(file) + for input_array, output_array in zip(inputs, outputs): + flattened_data = np.concatenate((input_array, output_array.real, output_array.imag)) + writer.writerow(flattened_data) def save_to_file_complex(inputs, outputs, filename, mode='a'): - with open(filename, mode, newline='') as file: - writer = csv.writer(file) - for input_array, output_array in zip(inputs, outputs): - flattened_data = np.concatenate((input_array.real, input_array.imag, output_array.real, output_array.imag)) - writer.writerow(flattened_data) - + with open(filename, mode, newline='') as file: + writer = csv.writer(file) + for input_array, output_array in zip(inputs, outputs): + flattened_data = np.concatenate((input_array.real, input_array.imag, output_array.real, output_array.imag)) + writer.writerow(flattened_data) + def generate_complex_inputs_2d(num_inputs, max_power): - for _ in range(num_inputs): - power = np.random.randint(1, max_power+1) - rows = 2 ** power - cols = 2 ** power - real_part = np.random.rand(rows, cols) - imag_part = np.random.rand(rows, cols) - complex_array = real_part + 1j * imag_part - yield complex_array + for _ in range(num_inputs): + power = np.random.randint(1, max_power+1) + rows = 2 ** power + cols = 2 ** power + real_part = np.random.rand(rows, cols) + imag_part = np.random.rand(rows, cols) + complex_array = real_part + 1j * imag_part + yield complex_array def compute_fft_2d(inputs): - total_time = 0 - num_calculations = 0 + total_time = 0 + num_calculations = 0 - for input_array in inputs: - start_time = time.time() - result = np.fft.fft2(input_array) - end_time = time.time() + for input_array in inputs: + start_time = time.time() + result = np.fft.fft2(input_array) + end_time = time.time() - total_time += end_time - start_time - num_calculations += 1 + total_time += end_time - start_time + num_calculations += 1 - if num_calculations % 1000 == 0: - average_time = total_time / num_calculations - print(f"Average execution time after {num_calculations} calculations: {average_time:.6f} seconds") + if num_calculations % 1000 == 0: + average_time = total_time / num_calculations + print(f"Average execution time after {num_calculations} calculations: {average_time:.6f} seconds") - yield result + yield result def compute_ifft_2d(inputs): - total_time = 0 - num_calculations = 0 + total_time = 0 + num_calculations = 0 - for input_array in inputs: - start_time = time.time() - result = np.fft.ifft2(input_array) - end_time = time.time() + for input_array in inputs: + start_time = time.time() + result = np.fft.ifft2(input_array) + end_time = time.time() - total_time += end_time - start_time - num_calculations += 1 + total_time += end_time - start_time + num_calculations += 1 - if num_calculations % 1000 == 0: - average_time = total_time / num_calculations - print(f"Average execution time after {num_calculations} calculations: {average_time:.6f} seconds") + if num_calculations % 1000 == 0: + average_time = total_time / num_calculations + print(f"Average execution time after {num_calculations} calculations: {average_time:.6f} seconds") - yield result + yield result def save_to_file_complex_2d(inputs, outputs, filename, mode='a'): - with open(filename, mode, newline='') as file: - writer = csv.writer(file) - for input_array, output_array in zip(inputs, outputs): - flattened_input = np.concatenate((input_array.real.flatten(), input_array.imag.flatten())) - flattened_output = np.concatenate((output_array.real.flatten(), output_array.imag.flatten())) - writer.writerow(np.concatenate((flattened_input, flattened_output))) + with open(filename, mode, newline='') as file: + writer = csv.writer(file) + for input_array, output_array in zip(inputs, outputs): + flattened_input = np.concatenate((input_array.real.flatten(), input_array.imag.flatten())) + flattened_output = np.concatenate((output_array.real.flatten(), output_array.imag.flatten())) + writer.writerow(np.concatenate((flattened_input, flattened_output))) # Parameters @@ -155,11 +155,11 @@ def save_to_file_complex_2d(inputs, outputs, filename, mode='a'): # Process and save in batches for i in range(0, num_inputs, batch_size): - current_batch = min(batch_size, num_inputs - i) - inputs = list(generate_inputs(current_batch, max_power)) - outputs = list(compute_fft(inputs)) - save_to_file(inputs, outputs, "fft_data.csv", mode='a' if i > 0 else 'w') - print(f"Batch {i//batch_size + 1} out of {num_inputs//batch_size} saved to fft_data.csv") + current_batch = min(batch_size, num_inputs - i) + inputs = list(generate_inputs(current_batch, max_power)) + outputs = list(compute_fft(inputs)) + save_to_file(inputs, outputs, "fft_data.csv", mode='a' if i > 0 else 'w') + print(f"Batch {i//batch_size + 1} out of {num_inputs//batch_size} saved to fft_data.csv") print("All data for fft processed and saved.") @@ -167,21 +167,21 @@ def save_to_file_complex_2d(inputs, outputs, filename, mode='a'): for i in range(0, num_inputs, batch_size): - current_batch = min(batch_size, num_inputs - i) - inputs = list(generate_inputs(current_batch, max_power)) - outputs = list(compute_ifft(inputs)) - save_to_file(inputs, outputs, "ifft_data.csv", mode='a' if i > 0 else 'w') - print(f"Batch {i//batch_size + 1} out of {num_inputs//batch_size} saved to ifft_data.csv") + current_batch = min(batch_size, num_inputs - i) + inputs = list(generate_inputs(current_batch, max_power)) + outputs = list(compute_ifft(inputs)) + save_to_file(inputs, outputs, "ifft_data.csv", mode='a' if i > 0 else 'w') + print(f"Batch {i//batch_size + 1} out of {num_inputs//batch_size} saved to ifft_data.csv") print("All real iff data processed and saved.") # Process and save in batches for i in range(0, num_inputs, batch_size): - current_batch = min(batch_size, num_inputs - i) - inputs = list(generate_complex_inputs(current_batch, max_power)) - outputs = list(compute_ifft(inputs)) - save_to_file_complex(inputs, outputs, "complex_ifft_data.csv", mode='a' if i > 0 else 'w') - print(f"Batch {i//batch_size + 1} out of {num_inputs//batch_size} saved to complex_ifft_data.csv") + current_batch = min(batch_size, num_inputs - i) + inputs = list(generate_complex_inputs(current_batch, max_power)) + outputs = list(compute_ifft(inputs)) + save_to_file_complex(inputs, outputs, "complex_ifft_data.csv", mode='a' if i > 0 else 'w') + print(f"Batch {i//batch_size + 1} out of {num_inputs//batch_size} saved to complex_ifft_data.csv") print("All complex ifft data processed and saved.") @@ -193,22 +193,22 @@ def save_to_file_complex_2d(inputs, outputs, filename, mode='a'): # Process and save 2D FFT data in batches filename_2d = "complex_fft_2d_data.csv" for i in range(0, num_inputs, batch_size): - current_batch = min(batch_size, num_inputs - i) - inputs_2d = list(generate_complex_inputs_2d(current_batch, max_power)) - outputs_2d = list(compute_fft_2d(inputs_2d)) - save_to_file_complex_2d(inputs_2d, outputs_2d, filename_2d, mode='a' if i > 0 else 'w') - print(f"Batch {i//batch_size + 1} out of {num_inputs//batch_size} saved to {filename_2d}") + current_batch = min(batch_size, num_inputs - i) + inputs_2d = list(generate_complex_inputs_2d(current_batch, max_power)) + outputs_2d = list(compute_fft_2d(inputs_2d)) + save_to_file_complex_2d(inputs_2d, outputs_2d, filename_2d, mode='a' if i > 0 else 'w') + print(f"Batch {i//batch_size + 1} out of {num_inputs//batch_size} saved to {filename_2d}") print("All complex 2D fft data processed and saved.") # Process and save 2D IFFT data in batches filename_2d = "complex_ifft_2d_data.csv" for i in range(0, num_inputs, batch_size): - current_batch = min(batch_size, num_inputs - i) - inputs_2d = list(generate_complex_inputs_2d(current_batch, max_power)) - outputs_2d = list(compute_ifft_2d(inputs_2d)) - save_to_file_complex_2d(inputs_2d, outputs_2d, filename_2d, mode='a' if i > 0 else 'w') - print(f"Batch {i//batch_size + 1} out of {num_inputs//batch_size} saved to {filename_2d}") + current_batch = min(batch_size, num_inputs - i) + inputs_2d = list(generate_complex_inputs_2d(current_batch, max_power)) + outputs_2d = list(compute_ifft_2d(inputs_2d)) + save_to_file_complex_2d(inputs_2d, outputs_2d, filename_2d, mode='a' if i > 0 else 'w') + print(f"Batch {i//batch_size + 1} out of {num_inputs//batch_size} saved to {filename_2d}") print("All complex 2D ifft data processed and saved.") diff --git a/src/test/java/org/apache/sysds/test/component/matrix/FourierTestWithFiles.java b/src/test/java/org/apache/sysds/test/component/matrix/FourierTestWithFiles.java index acf8738c60c..fe051e2c272 100644 --- a/src/test/java/org/apache/sysds/test/component/matrix/FourierTestWithFiles.java +++ b/src/test/java/org/apache/sysds/test/component/matrix/FourierTestWithFiles.java @@ -29,425 +29,425 @@ import static org.apache.sysds.runtime.matrix.data.LibMatrixFourier.ifft; public class FourierTestWithFiles { - int progressInterval = 5000; + int progressInterval = 5000; - // prior to executing the following tests it is necessary to run the Numpy Script in FourierTestData.py - // and add the generated files to the root of the project. - @Test - public void testFftWithNumpyData() throws IOException { + // prior to executing the following tests it is necessary to run the Numpy Script in FourierTestData.py + // and add the generated files to the root of the project. + @Test + public void testFftWithNumpyData() throws IOException { - String filename = "fft_data.csv"; // Path to your CSV file - String path = "./src/test/java/org/apache/sysds/test/component/matrix/"; + String filename = "fft_data.csv"; // Path to your CSV file + String path = "./src/test/java/org/apache/sysds/test/component/matrix/"; - BufferedReader reader = new BufferedReader(new FileReader(path+filename)); - String line; - int lineNumber = 0; - long totalTime = 0; // Total time for all FFT computations - int numCalculations = 0; // Number of FFT computations + BufferedReader reader = new BufferedReader(new FileReader(path+filename)); + String line; + int lineNumber = 0; + long totalTime = 0; // Total time for all FFT computations + int numCalculations = 0; // Number of FFT computations - while ((line = reader.readLine()) != null) { - lineNumber++; + while ((line = reader.readLine()) != null) { + lineNumber++; - String[] values = line.split(","); - int n = values.length / 3; + String[] values = line.split(","); + int n = values.length / 3; - double[] re = new double[n]; - double[] im = new double[n]; + double[] re = new double[n]; + double[] im = new double[n]; - double[][] expected = new double[2][n]; // First row for real, second row for imaginary parts + double[][] expected = new double[2][n]; // First row for real, second row for imaginary parts - for (int i = 0; i < n; i++) { - re[i] = Double.parseDouble(values[i]); - expected[0][i] = Double.parseDouble(values[n + i]); // Real part - expected[1][i] = Double.parseDouble(values[n * 2 + i]); // Imaginary part - } + for (int i = 0; i < n; i++) { + re[i] = Double.parseDouble(values[i]); + expected[0][i] = Double.parseDouble(values[n + i]); // Real part + expected[1][i] = Double.parseDouble(values[n * 2 + i]); // Imaginary part + } - long startTime = System.nanoTime(); - fft(re, im, 1, n); - long endTime = System.nanoTime(); + long startTime = System.nanoTime(); + fft(re, im, 1, n); + long endTime = System.nanoTime(); - if(lineNumber > 1000){ - totalTime += (endTime - startTime); - numCalculations++; + if(lineNumber > 1000){ + totalTime += (endTime - startTime); + numCalculations++; - if (numCalculations % progressInterval == 0) { - double averageTime = (totalTime / 1e6) / numCalculations; // Average time in milliseconds - System.out.println("fft(double[][][] in): Average execution time after " + numCalculations + " calculations: " + String.format("%.8f", averageTime/1000) + " s"); - } - } + if (numCalculations % progressInterval == 0) { + double averageTime = (totalTime / 1e6) / numCalculations; // Average time in milliseconds + System.out.println("fft(double[][][] in): Average execution time after " + numCalculations + " calculations: " + String.format("%.8f", averageTime/1000) + " s"); + } + } - double[][] actual = {re, im}; + double[][] actual = {re, im}; - // Validate the FFT results - validateFftResults(expected, actual, lineNumber); - } + // Validate the FFT results + validateFftResults(expected, actual, lineNumber); + } - reader.close(); - } + reader.close(); + } - private void validateFftResults(double[][] expected, double[][] actual, int lineNumber) { + private void validateFftResults(double[][] expected, double[][] actual, int lineNumber) { - int length = expected[0].length; + int length = expected[0].length; - for (int i = 0; i < length; i++) { - double realActual = actual[0][i]; - double imagActual = actual[1][i]; - assertEquals("Mismatch in real part at index " + i + " in line " + lineNumber, expected[0][i], realActual, 1e-9); - assertEquals("Mismatch in imaginary part at index " + i + " in line " + lineNumber, expected[1][i], imagActual, 1e-9); - } + for (int i = 0; i < length; i++) { + double realActual = actual[0][i]; + double imagActual = actual[1][i]; + assertEquals("Mismatch in real part at index " + i + " in line " + lineNumber, expected[0][i], realActual, 1e-9); + assertEquals("Mismatch in imaginary part at index " + i + " in line " + lineNumber, expected[1][i], imagActual, 1e-9); + } - if(lineNumber % progressInterval == 0){ - System.out.println("fft(double[][][] in): Finished processing line " + lineNumber); - } - - } + if(lineNumber % progressInterval == 0){ + System.out.println("fft(double[][][] in): Finished processing line " + lineNumber); + } - @Test - public void testFftExecutionTime() throws IOException { + } - String filename = "fft_data.csv"; // Path to your CSV file - String path = "./src/test/java/org/apache/sysds/test/component/matrix/"; + @Test + public void testFftExecutionTime() throws IOException { - BufferedReader reader = new BufferedReader(new FileReader(path+filename)); - String line; - int lineNumber = 0; - long totalTime = 0; // Total time for all FFT computations - int numCalculations = 0; // Number of FFT computations + String filename = "fft_data.csv"; // Path to your CSV file + String path = "./src/test/java/org/apache/sysds/test/component/matrix/"; - while ((line = reader.readLine()) != null) { + BufferedReader reader = new BufferedReader(new FileReader(path+filename)); + String line; + int lineNumber = 0; + long totalTime = 0; // Total time for all FFT computations + int numCalculations = 0; // Number of FFT computations - lineNumber++; - String[] values = line.split(","); - int n = values.length / 3; + while ((line = reader.readLine()) != null) { - double[] re = new double[n]; - double[] im = new double[n]; + lineNumber++; + String[] values = line.split(","); + int n = values.length / 3; - for (int i = 0; i < n; i++) { - re[i] = Double.parseDouble(values[i]); // Real part - im[i] = Double.parseDouble(values[n + i]); // Imaginary part - } + double[] re = new double[n]; + double[] im = new double[n]; - long startTime = System.nanoTime(); + for (int i = 0; i < n; i++) { + re[i] = Double.parseDouble(values[i]); // Real part + im[i] = Double.parseDouble(values[n + i]); // Imaginary part + } - fft(re, im, 1, n); + long startTime = System.nanoTime(); - long endTime = System.nanoTime(); + fft(re, im, 1, n); - if(lineNumber > 1000){ - totalTime += (endTime - startTime); - numCalculations++; + long endTime = System.nanoTime(); - if (numCalculations % progressInterval == 0) { - double averageTime = (totalTime / 1e6) / numCalculations; // Average time in milliseconds - System.out.println("fft_old(double[][][] in, boolean calcInv) Average execution time after " + numCalculations + " calculations: " + String.format("%.8f", averageTime/1000) + " s"); - } - } - } + if(lineNumber > 1000){ + totalTime += (endTime - startTime); + numCalculations++; - reader.close(); - } + if (numCalculations % progressInterval == 0) { + double averageTime = (totalTime / 1e6) / numCalculations; // Average time in milliseconds + System.out.println("fft_old(double[][][] in, boolean calcInv) Average execution time after " + numCalculations + " calculations: " + String.format("%.8f", averageTime/1000) + " s"); + } + } + } - @Test - public void testFftExecutionTimeOfOneDimFFT() throws IOException { + reader.close(); + } - String filename = "fft_data.csv"; // Path to your CSV file - String path = "./src/test/java/org/apache/sysds/test/component/matrix/"; + @Test + public void testFftExecutionTimeOfOneDimFFT() throws IOException { - BufferedReader reader = new BufferedReader(new FileReader(path+filename)); - String line; - int lineNumber = 0; - long totalTime = 0; // Total time for all FFT computations - int numCalculations = 0; // Number of FFT computations + String filename = "fft_data.csv"; // Path to your CSV file + String path = "./src/test/java/org/apache/sysds/test/component/matrix/"; - while ((line = reader.readLine()) != null) { + BufferedReader reader = new BufferedReader(new FileReader(path+filename)); + String line; + int lineNumber = 0; + long totalTime = 0; // Total time for all FFT computations + int numCalculations = 0; // Number of FFT computations - lineNumber++; - String[] values = line.split(","); - int n = values.length / 2; + while ((line = reader.readLine()) != null) { - double[] re = new double[n]; - double[] im = new double[n]; + lineNumber++; + String[] values = line.split(","); + int n = values.length / 2; - for (int i = 0; i < n; i++) { - re[i] = Double.parseDouble(values[i]); // Real part - im[i] = Double.parseDouble(values[n + i]); // Imaginary part - } + double[] re = new double[n]; + double[] im = new double[n]; - long startTime = System.nanoTime(); - // one dimensional - fft(re, im, 1, n); + for (int i = 0; i < n; i++) { + re[i] = Double.parseDouble(values[i]); // Real part + im[i] = Double.parseDouble(values[n + i]); // Imaginary part + } - long endTime = System.nanoTime(); + long startTime = System.nanoTime(); + // one dimensional + fft(re, im, 1, n); - if(lineNumber > 1000){ - totalTime += (endTime - startTime); - numCalculations++; + long endTime = System.nanoTime(); - if (numCalculations % progressInterval == 0) { - double averageTime = (totalTime / 1e6) / numCalculations; // Average time in milliseconds - System.out.println("fft_one_dim: Average execution time after " + numCalculations + " calculations: " + String.format("%.8f", averageTime/1000) + " s "); - } - } - } + if(lineNumber > 1000){ + totalTime += (endTime - startTime); + numCalculations++; - reader.close(); - } + if (numCalculations % progressInterval == 0) { + double averageTime = (totalTime / 1e6) / numCalculations; // Average time in milliseconds + System.out.println("fft_one_dim: Average execution time after " + numCalculations + " calculations: " + String.format("%.8f", averageTime/1000) + " s "); + } + } + } - // prior to executing this test it is necessary to run the Numpy Script in FourierTestData.py and add the generated file to the root of the project. - @Test - public void testIfftWithRealNumpyData() throws IOException { + reader.close(); + } - String filename = "ifft_data.csv"; // Path to your CSV file - String path = "./src/test/java/org/apache/sysds/test/component/matrix/"; + // prior to executing this test it is necessary to run the Numpy Script in FourierTestData.py and add the generated file to the root of the project. + @Test + public void testIfftWithRealNumpyData() throws IOException { - BufferedReader reader = new BufferedReader(new FileReader(path+filename)); - String line; - int lineNumber = 0; - - while ((line = reader.readLine()) != null) { + String filename = "ifft_data.csv"; // Path to your CSV file + String path = "./src/test/java/org/apache/sysds/test/component/matrix/"; - lineNumber++; - String[] values = line.split(","); - int n = values.length / 3; + BufferedReader reader = new BufferedReader(new FileReader(path+filename)); + String line; + int lineNumber = 0; - double[] re = new double[n]; - double[] im = new double[n]; + while ((line = reader.readLine()) != null) { - double[][] expected = new double[2][n]; // First row for real, second row for imaginary parts - - for (int i = 0; i < n; i++) { - re[i] = Double.parseDouble(values[i]); // Real part of input - // Imaginary part of input is assumed to be 0 - expected[0][i] = Double.parseDouble(values[n + i]); // Real part of expected output - expected[1][i] = Double.parseDouble(values[n * 2 + i]); // Imaginary part of expected output - } + lineNumber++; + String[] values = line.split(","); + int n = values.length / 3; - ifft(re, im, 1, n); // Perform IFFT + double[] re = new double[n]; + double[] im = new double[n]; - double[][] actual = new double[][]{re, im}; - // Validate the IFFT results - validateFftResults(expected, actual, lineNumber); - } - - reader.close(); + double[][] expected = new double[2][n]; // First row for real, second row for imaginary parts - } + for (int i = 0; i < n; i++) { + re[i] = Double.parseDouble(values[i]); // Real part of input + // Imaginary part of input is assumed to be 0 + expected[0][i] = Double.parseDouble(values[n + i]); // Real part of expected output + expected[1][i] = Double.parseDouble(values[n * 2 + i]); // Imaginary part of expected output + } - @Test - public void testIfftWithComplexNumpyData() throws IOException { + ifft(re, im, 1, n); // Perform IFFT - String filename = "complex_ifft_data.csv"; // Adjusted path to your IFFT data file with complex inputs - String path = "./src/test/java/org/apache/sysds/test/component/matrix/"; + double[][] actual = new double[][]{re, im}; + // Validate the IFFT results + validateFftResults(expected, actual, lineNumber); + } - BufferedReader reader = new BufferedReader(new FileReader(path+filename)); - String line; - int lineNumber = 0; - long totalTime = 0; // Total time for all IFFT computations - int numCalculations = 0; // Number of IFFT computations - - while ((line = reader.readLine()) != null) { - lineNumber++; - String[] values = line.split(","); - int n = values.length / 4; // Adjusted for complex numbers - - // Real and imaginary parts - double[] re = new double[n]; - double[] im = new double[n]; - - double[][] expected = new double[2][n]; // Expected real and imaginary parts - - for (int i = 0; i < n; i++) { - re[i] = Double.parseDouble(values[i]); // Real part of input - im[i] = Double.parseDouble(values[i + n]); // Imaginary part of input - expected[0][i] = Double.parseDouble(values[i + 2 * n]); // Expected real part - expected[1][i] = Double.parseDouble(values[i + 3 * n]); // Expected imaginary part - } - - long startTime = System.nanoTime(); - - ifft(re, im, 1, n); // Perform IFFT - - long endTime = System.nanoTime(); - - if (lineNumber > 1000) { - totalTime += (endTime - startTime); - numCalculations++; - } - - double[][] actual = new double[][]{re, im}; - // Validate the IFFT results - validateComplexIFftResults(expected, actual, lineNumber); - - if (lineNumber % progressInterval == 0) { - double averageTime = (totalTime / 1e6) / numCalculations; // Average time in milliseconds - System.out.println("ifft: Average execution time after " + numCalculations + " calculations: " + String.format("%.8f", averageTime / 1000) + " s"); - } - } - - reader.close(); - } - - private void validateComplexIFftResults(double[][] expected, double[][] actual, int lineNumber) { - - int length = expected[0].length; - - for (int i = 0; i < length; i++) { - double realActual = actual[0][i]; - double imagActual = actual[1][i]; - assertEquals("Mismatch in real part at index " + i + " in line " + lineNumber, expected[0][i], realActual, 1e-9); - assertEquals("Mismatch in imaginary part at index " + i + " in line " + lineNumber, expected[1][i], imagActual, 1e-9); - } - - if (lineNumber % progressInterval == 0) { - System.out.println("ifft(complex input): Finished processing line " + lineNumber); - } - - } + reader.close(); - @Test - public void testFft2dWithNumpyData() throws IOException { + } - String filename = "complex_fft_2d_data.csv"; // path to your 2D FFT data file - String path = "./src/test/java/org/apache/sysds/test/component/matrix/"; + @Test + public void testIfftWithComplexNumpyData() throws IOException { - BufferedReader reader = new BufferedReader(new FileReader(path+filename)); - String line; - int lineNumber = 0; - long totalTime = 0; // Total time for all FFT 2D computations - int numCalculations = 0; // Number of FFT 2D computations + String filename = "complex_ifft_data.csv"; // Adjusted path to your IFFT data file with complex inputs + String path = "./src/test/java/org/apache/sysds/test/component/matrix/"; - while ((line = reader.readLine()) != null) { - lineNumber++; - String[] values = line.split(","); - int halfLength = values.length / 4; - int sideLength = (int) Math.sqrt(halfLength); // Assuming square matrix + BufferedReader reader = new BufferedReader(new FileReader(path+filename)); + String line; + int lineNumber = 0; + long totalTime = 0; // Total time for all IFFT computations + int numCalculations = 0; // Number of IFFT computations - // Real and imaginary parts - double[] re = new double[sideLength*sideLength]; - double[] im = new double[sideLength*sideLength]; - - double[][][] expected = new double[2][sideLength][sideLength]; - - for (int i = 0; i < halfLength; i++) { - int row = i / sideLength; - int col = i % sideLength; + while ((line = reader.readLine()) != null) { + lineNumber++; + String[] values = line.split(","); + int n = values.length / 4; // Adjusted for complex numbers - // i == row*sideLength+col?! - re[row*sideLength+col] = Double.parseDouble(values[i]); - im[row*sideLength+col] = Double.parseDouble(values[i + halfLength]); - expected[0][row][col] = Double.parseDouble(values[i + 2 * halfLength]); - expected[1][row][col] = Double.parseDouble(values[i + 3 * halfLength]); - } + // Real and imaginary parts + double[] re = new double[n]; + double[] im = new double[n]; - long startTime = System.nanoTime(); - // Use your fft2d implementation - fft(re, im, sideLength, sideLength); - //double[][][] javaFftResult = fft_old(input, false); // Use your fft2d implementation - long endTime = System.nanoTime(); - totalTime += (endTime - startTime); - numCalculations++; + double[][] expected = new double[2][n]; // Expected real and imaginary parts - for (int i = 0; i < sideLength; i++) { - for (int j = 0; j < sideLength; j++) { - assertComplexEquals("Mismatch at [" + i + "][" + j + "] in line " + lineNumber, - expected[0][i][j], expected[1][i][j], - re[i*sideLength+j], im[i*sideLength+j]); - } - } + for (int i = 0; i < n; i++) { + re[i] = Double.parseDouble(values[i]); // Real part of input + im[i] = Double.parseDouble(values[i + n]); // Imaginary part of input + expected[0][i] = Double.parseDouble(values[i + 2 * n]); // Expected real part + expected[1][i] = Double.parseDouble(values[i + 3 * n]); // Expected imaginary part + } - if (lineNumber % progressInterval == 0) { - double averageTime = (totalTime / 1e6) / numCalculations; // Average time in milliseconds - System.out.println("fft2d: Average execution time after " + numCalculations + " calculations: " + String.format("%.8f", averageTime/1000) + " s"); - } - } + long startTime = System.nanoTime(); - reader.close(); - System.out.println("fft2d: Finished processing " + lineNumber + " lines.\n Average execution time: " + String.format("%.8f", (totalTime / 1e6 / numCalculations)/1000) + " s"); + ifft(re, im, 1, n); // Perform IFFT - } + long endTime = System.nanoTime(); + if (lineNumber > 1000) { + totalTime += (endTime - startTime); + numCalculations++; + } - @Test - public void testIfft2dWithNumpyData() throws IOException { - - String filename = "complex_ifft_2d_data.csv"; // path to your 2D IFFT data file - String path = "./src/test/java/org/apache/sysds/test/component/matrix/"; - - BufferedReader reader = new BufferedReader(new FileReader(path+filename)); - String line; - int lineNumber = 0; - long totalTime = 0; // Total time for all IFFT 2D computations - int numCalculations = 0; // Number of IFFT 2D computations - - while ((line = reader.readLine()) != null) { - - lineNumber++; - String[] values = line.split(","); - int halfLength = values.length / 4; - int sideLength = (int) Math.sqrt(halfLength); // Assuming square matrix + double[][] actual = new double[][]{re, im}; + // Validate the IFFT results + validateComplexIFftResults(expected, actual, lineNumber); - // Real and imaginary parts - double[] re = new double[sideLength*sideLength]; - double[] im = new double[sideLength*sideLength]; - - double[][][] expected = new double[2][sideLength][sideLength]; - - for (int i = 0; i < halfLength; i++) { - int row = i / sideLength; - int col = i % sideLength; - - re[row*sideLength+col] = Double.parseDouble(values[i]); - im[row*sideLength+col] = Double.parseDouble(values[i + halfLength]); - - expected[0][row][col] = Double.parseDouble(values[i + 2 * halfLength]); - expected[1][row][col] = Double.parseDouble(values[i + 3 * halfLength]); - } - - long startTime = System.nanoTime(); - - // Use your ifft2d implementation - ifft(re, im, sideLength, sideLength); - - long endTime = System.nanoTime(); - if(lineNumber > 1000){ - totalTime += (endTime - startTime); - numCalculations++; - } - - for (int i = 0; i < sideLength; i++) { - for (int j = 0; j < sideLength; j++) { - assertComplexEquals("Mismatch at [" + i + "][" + j + "] in line " + lineNumber, - expected[0][i][j], expected[1][i][j], - re[i*sideLength+j], im[i*sideLength+j]); - } - } + if (lineNumber % progressInterval == 0) { + double averageTime = (totalTime / 1e6) / numCalculations; // Average time in milliseconds + System.out.println("ifft: Average execution time after " + numCalculations + " calculations: " + String.format("%.8f", averageTime / 1000) + " s"); + } + } - if (lineNumber % progressInterval == 0) { - System.out.println("fft2d/ifft2d: Finished processing line " + lineNumber); - double averageTime = (totalTime / 1e6) / numCalculations; // Average time in milliseconds - System.out.println("Ifft2d Average execution time after " + numCalculations + " calculations: " + String.format("%.8f", averageTime/1000) + " s"); - } - } - - reader.close(); - System.out.println("ifft2d: Finished processing " + lineNumber + " lines.\n Average execution time: " + String.format("%.8f", (totalTime / 1e6 / numCalculations)/1000) + " ms"); - - } - - // Helper method for asserting equality with a tolerance - private static void assertEquals(String message, double expected, double actual, double tolerance) { - assertTrue(message + " - Expected: " + expected + ", Actual: " + actual, Math.abs(expected - actual) <= tolerance); - } + reader.close(); + } - private void assertComplexEquals(String message, double expectedReal, double expectedImag, double actualReal, double actualImag) { - - final double EPSILON = 1e-9; - assertTrue(message + " - Mismatch in real part. Expected: " + expectedReal + ", Actual: " + actualReal, - Math.abs(expectedReal - actualReal) <= EPSILON); - assertTrue(message + " - Mismatch in imaginary part. Expected: " + expectedImag + ", Actual: " + actualImag, - Math.abs(expectedImag - actualImag) <= EPSILON); + private void validateComplexIFftResults(double[][] expected, double[][] actual, int lineNumber) { - } + int length = expected[0].length; + + for (int i = 0; i < length; i++) { + double realActual = actual[0][i]; + double imagActual = actual[1][i]; + assertEquals("Mismatch in real part at index " + i + " in line " + lineNumber, expected[0][i], realActual, 1e-9); + assertEquals("Mismatch in imaginary part at index " + i + " in line " + lineNumber, expected[1][i], imagActual, 1e-9); + } + + if (lineNumber % progressInterval == 0) { + System.out.println("ifft(complex input): Finished processing line " + lineNumber); + } + + } + + @Test + public void testFft2dWithNumpyData() throws IOException { + + String filename = "complex_fft_2d_data.csv"; // path to your 2D FFT data file + String path = "./src/test/java/org/apache/sysds/test/component/matrix/"; + + BufferedReader reader = new BufferedReader(new FileReader(path+filename)); + String line; + int lineNumber = 0; + long totalTime = 0; // Total time for all FFT 2D computations + int numCalculations = 0; // Number of FFT 2D computations + + while ((line = reader.readLine()) != null) { + lineNumber++; + String[] values = line.split(","); + int halfLength = values.length / 4; + int sideLength = (int) Math.sqrt(halfLength); // Assuming square matrix + + // Real and imaginary parts + double[] re = new double[sideLength*sideLength]; + double[] im = new double[sideLength*sideLength]; + + double[][][] expected = new double[2][sideLength][sideLength]; + + for (int i = 0; i < halfLength; i++) { + int row = i / sideLength; + int col = i % sideLength; + + // i == row*sideLength+col?! + re[row*sideLength+col] = Double.parseDouble(values[i]); + im[row*sideLength+col] = Double.parseDouble(values[i + halfLength]); + expected[0][row][col] = Double.parseDouble(values[i + 2 * halfLength]); + expected[1][row][col] = Double.parseDouble(values[i + 3 * halfLength]); + } + + long startTime = System.nanoTime(); + // Use your fft2d implementation + fft(re, im, sideLength, sideLength); + //double[][][] javaFftResult = fft_old(input, false); // Use your fft2d implementation + long endTime = System.nanoTime(); + totalTime += (endTime - startTime); + numCalculations++; + + for (int i = 0; i < sideLength; i++) { + for (int j = 0; j < sideLength; j++) { + assertComplexEquals("Mismatch at [" + i + "][" + j + "] in line " + lineNumber, + expected[0][i][j], expected[1][i][j], + re[i*sideLength+j], im[i*sideLength+j]); + } + } + + if (lineNumber % progressInterval == 0) { + double averageTime = (totalTime / 1e6) / numCalculations; // Average time in milliseconds + System.out.println("fft2d: Average execution time after " + numCalculations + " calculations: " + String.format("%.8f", averageTime/1000) + " s"); + } + } + + reader.close(); + System.out.println("fft2d: Finished processing " + lineNumber + " lines.\n Average execution time: " + String.format("%.8f", (totalTime / 1e6 / numCalculations)/1000) + " s"); + + } + + + @Test + public void testIfft2dWithNumpyData() throws IOException { + + String filename = "complex_ifft_2d_data.csv"; // path to your 2D IFFT data file + String path = "./src/test/java/org/apache/sysds/test/component/matrix/"; + + BufferedReader reader = new BufferedReader(new FileReader(path+filename)); + String line; + int lineNumber = 0; + long totalTime = 0; // Total time for all IFFT 2D computations + int numCalculations = 0; // Number of IFFT 2D computations + + while ((line = reader.readLine()) != null) { + + lineNumber++; + String[] values = line.split(","); + int halfLength = values.length / 4; + int sideLength = (int) Math.sqrt(halfLength); // Assuming square matrix + + // Real and imaginary parts + double[] re = new double[sideLength*sideLength]; + double[] im = new double[sideLength*sideLength]; + + double[][][] expected = new double[2][sideLength][sideLength]; + + for (int i = 0; i < halfLength; i++) { + int row = i / sideLength; + int col = i % sideLength; + + re[row*sideLength+col] = Double.parseDouble(values[i]); + im[row*sideLength+col] = Double.parseDouble(values[i + halfLength]); + + expected[0][row][col] = Double.parseDouble(values[i + 2 * halfLength]); + expected[1][row][col] = Double.parseDouble(values[i + 3 * halfLength]); + } + + long startTime = System.nanoTime(); + + // Use your ifft2d implementation + ifft(re, im, sideLength, sideLength); + + long endTime = System.nanoTime(); + if(lineNumber > 1000){ + totalTime += (endTime - startTime); + numCalculations++; + } + + for (int i = 0; i < sideLength; i++) { + for (int j = 0; j < sideLength; j++) { + assertComplexEquals("Mismatch at [" + i + "][" + j + "] in line " + lineNumber, + expected[0][i][j], expected[1][i][j], + re[i*sideLength+j], im[i*sideLength+j]); + } + } + + if (lineNumber % progressInterval == 0) { + System.out.println("fft2d/ifft2d: Finished processing line " + lineNumber); + double averageTime = (totalTime / 1e6) / numCalculations; // Average time in milliseconds + System.out.println("Ifft2d Average execution time after " + numCalculations + " calculations: " + String.format("%.8f", averageTime/1000) + " s"); + } + } + + reader.close(); + System.out.println("ifft2d: Finished processing " + lineNumber + " lines.\n Average execution time: " + String.format("%.8f", (totalTime / 1e6 / numCalculations)/1000) + " ms"); + + } + + // Helper method for asserting equality with a tolerance + private static void assertEquals(String message, double expected, double actual, double tolerance) { + assertTrue(message + " - Expected: " + expected + ", Actual: " + actual, Math.abs(expected - actual) <= tolerance); + } + + private void assertComplexEquals(String message, double expectedReal, double expectedImag, double actualReal, double actualImag) { + + final double EPSILON = 1e-9; + assertTrue(message + " - Mismatch in real part. Expected: " + expectedReal + ", Actual: " + actualReal, + Math.abs(expectedReal - actualReal) <= EPSILON); + assertTrue(message + " - Mismatch in imaginary part. Expected: " + expectedImag + ", Actual: " + actualImag, + Math.abs(expectedImag - actualImag) <= EPSILON); + + } } diff --git a/src/test/java/org/apache/sysds/test/component/matrix/LibMatrixSTFTTest.java b/src/test/java/org/apache/sysds/test/component/matrix/LibMatrixSTFTTest.java index 9377c1d3a28..e2ed4a622c0 100644 --- a/src/test/java/org/apache/sysds/test/component/matrix/LibMatrixSTFTTest.java +++ b/src/test/java/org/apache/sysds/test/component/matrix/LibMatrixSTFTTest.java @@ -28,77 +28,77 @@ public class LibMatrixSTFTTest { - @Test - public void simple_test() { + @Test + public void simple_test() { - double[] signal = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}; - int windowSize = 4; - int overlap = 2; - double[][] stftResult = one_dim_stft(signal, windowSize, overlap); + double[] signal = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}; + int windowSize = 4; + int overlap = 2; + double[][] stftResult = one_dim_stft(signal, windowSize, overlap); - // 1st row real part, 2nd row imaginary part - double[][] expected = {{6, -2, -2, -2, 14, -2, -2, -2, 22, -2, -2, -2, 30, -2, -2, -2, 38, -2, -2, -2, 46, -2, -2, -2, 54, -2, -2, -2},{0, 2, 0, -2, 0, 2, 0, -2, 0, 2, 0, -2, 0, 2, 0, -2, 0, 2, 0, -2, 0, 2, 0, -2, 0, 2, 0, -2}}; + // 1st row real part, 2nd row imaginary part + double[][] expected = {{6, -2, -2, -2, 14, -2, -2, -2, 22, -2, -2, -2, 30, -2, -2, -2, 38, -2, -2, -2, 46, -2, -2, -2, 54, -2, -2, -2},{0, 2, 0, -2, 0, 2, 0, -2, 0, 2, 0, -2, 0, 2, 0, -2, 0, 2, 0, -2, 0, 2, 0, -2, 0, 2, 0, -2}}; - for(double[] row : stftResult){ - for (double elem : row){ - System.out.print(elem + " "); - } - System.out.println(); - } + for(double[] row : stftResult){ + for (double elem : row){ + System.out.print(elem + " "); + } + System.out.println(); + } - assertArrayEquals(expected[0], stftResult[0], 0.0001); - assertArrayEquals(expected[1], stftResult[1], 0.0001); + assertArrayEquals(expected[0], stftResult[0], 0.0001); + assertArrayEquals(expected[1], stftResult[1], 0.0001); - } + } - @Test - public void matrix_block_one_dim_test(){ + @Test + public void matrix_block_one_dim_test(){ - double[] in = {0, 18, -15, 3}; + double[] in = {0, 18, -15, 3}; - double[] expected_re = {6,15,-36,15}; - double[] expected_im = {0,-15,0,15}; + double[] expected_re = {6,15,-36,15}; + double[] expected_im = {0,-15,0,15}; - MatrixBlock[] res = stft(in, 4, 0); - double[] res_re = res[0].getDenseBlockValues(); - double[] res_im = res[1].getDenseBlockValues(); + MatrixBlock[] res = stft(in, 4, 0); + double[] res_re = res[0].getDenseBlockValues(); + double[] res_im = res[1].getDenseBlockValues(); - for(double elem : res_re){ - System.out.print(elem+" "); - } - System.out.println(); - for(double elem : res_im){ - System.out.print(elem+" "); - } + for(double elem : res_re){ + System.out.print(elem+" "); + } + System.out.println(); + for(double elem : res_im){ + System.out.print(elem+" "); + } - assertArrayEquals(expected_re, res_re, 0.0001); - assertArrayEquals(expected_im, res_im, 0.0001); + assertArrayEquals(expected_re, res_re, 0.0001); + assertArrayEquals(expected_im, res_im, 0.0001); - } + } - @Test - public void matrix_block_one_dim_test2(){ + @Test + public void matrix_block_one_dim_test2(){ - double[] in = {10, 5, -3, 8, 15, -6, 2, 0}; + double[] in = {10, 5, -3, 8, 15, -6, 2, 0}; - double[] expected_re = {20.0, 13.0, -6.0, 13.0, 14.0, -18.0, 10.0, -18.0, 11.0, 13.0, 23.0, 13.0}; - double[] expected_im = {0.0, 3.0, 0.0, -3.0, 0.0, -14.0, 0.0, 14.0, 0.0, 6.0, 0.0, -6.0 }; + double[] expected_re = {20.0, 13.0, -6.0, 13.0, 14.0, -18.0, 10.0, -18.0, 11.0, 13.0, 23.0, 13.0}; + double[] expected_im = {0.0, 3.0, 0.0, -3.0, 0.0, -14.0, 0.0, 14.0, 0.0, 6.0, 0.0, -6.0 }; - MatrixBlock[] res = stft(in, 4, 2); - double[] res_re = res[0].getDenseBlockValues(); - double[] res_im = res[1].getDenseBlockValues(); + MatrixBlock[] res = stft(in, 4, 2); + double[] res_re = res[0].getDenseBlockValues(); + double[] res_im = res[1].getDenseBlockValues(); - for(double elem : res_re){ - System.out.print(elem+" "); - } - System.out.println(); - for(double elem : res_im){ - System.out.print(elem+" "); - } + for(double elem : res_re){ + System.out.print(elem+" "); + } + System.out.println(); + for(double elem : res_im){ + System.out.print(elem+" "); + } - assertArrayEquals(expected_re, res_re, 0.0001); - assertArrayEquals(expected_im, res_im, 0.0001); + assertArrayEquals(expected_re, res_re, 0.0001); + assertArrayEquals(expected_im, res_im, 0.0001); - } + } } From fc08367f1c0a7b55368790fba0ca83e41d2fae24 Mon Sep 17 00:00:00 2001 From: Jessica Priebe Date: Thu, 18 Jan 2024 14:35:30 +0100 Subject: [PATCH 039/133] java doc LibCommonsMath and LibMatrixFourier --- .../runtime/matrix/data/LibCommonsMath.java | 12 ++++ .../runtime/matrix/data/LibMatrixFourier.java | 66 +++++++++++++++++-- .../test/component/matrix/FourierTest.java | 31 +++------ 3 files changed, 83 insertions(+), 26 deletions(-) diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java index a6bbc1ef6b3..d18c0c40e70 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java @@ -264,6 +264,12 @@ private static EigenDecomposition computeEigenRegularized(MatrixBlock in) { DataConverter.convertToArray2DRowRealMatrix(in2)); } + /** + * Function to perform FFT on a given matrix. + * + * @param in matrix object + * @return array of matrix blocks + */ private static MatrixBlock[] computeFFT(MatrixBlock in) { if( in == null || in.isEmptyBlock(false) ) throw new DMLRuntimeException("Invalid empty block"); @@ -273,6 +279,12 @@ private static MatrixBlock[] computeFFT(MatrixBlock in) { return fft(in); } + /** + * Function to perform IFFT on a given matrix. + * + * @param in matrix object + * @return array of matrix blocks + */ private static MatrixBlock[] computeIFFT(MatrixBlock in) { if( in == null || in.isEmptyBlock(false)) throw new DMLRuntimeException("Invalid empty block"); diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java index e8283254191..df5b7337ddc 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java @@ -24,9 +24,14 @@ public class LibMatrixFourier { /** - * Function to perform Fast Fourier Transformation + * Function to perform FFT on two given matrices. + * The first one represents the real values and the second one the imaginary values. + * The output also contains one matrix for the real and one for the imaginary values. + * + * @param re matrix object representing the real values + * @param im matrix object representing the imaginary values + * @return array of two matrix blocks */ - public static MatrixBlock[] fft(MatrixBlock re, MatrixBlock im){ int rows = re.getNumRows(); @@ -38,6 +43,15 @@ public static MatrixBlock[] fft(MatrixBlock re, MatrixBlock im){ return new MatrixBlock[]{re, im}; } + /** + * Function to perform IFFT on two given matrices. + * The first one represents the real values and the second one the imaginary values. + * The output also contains one matrix for the real and one for the imaginary values. + * + * @param re matrix object representing the real values + * @param im matrix object representing the imaginary values + * @return array of two matrix blocks + */ public static MatrixBlock[] ifft(MatrixBlock re, MatrixBlock im){ int rows = re.getNumRows(); @@ -49,6 +63,16 @@ public static MatrixBlock[] ifft(MatrixBlock re, MatrixBlock im){ return new MatrixBlock[]{re, im}; } + /** + * Function to perform FFT on two given double arrays. + * The first one represents the real values and the second one the imaginary values. + * Both arrays get updated and contain the result. + * + * @param re array representing the real values + * @param im array representing the imaginary values + * @param rows number of rows + * @param cols number of rows + */ public static void fft(double[] re, double[] im, int rows, int cols) { double[] re_inter = new double[rows*cols]; @@ -64,6 +88,16 @@ public static void fft(double[] re, double[] im, int rows, int cols) { } + /** + * Function to perform IFFT on two given double arrays. + * The first one represents the real values and the second one the imaginary values. + * Both arrays get updated and contain the result. + * + * @param re array representing the real values + * @param im array representing the imaginary values + * @param rows number of rows + * @param cols number of rows + */ public static void ifft(double[] re, double[] im, int rows, int cols) { double[] re_inter = new double[rows*cols]; @@ -79,7 +113,7 @@ public static void ifft(double[] re, double[] im, int rows, int cols) { } - public static void fft_one_dim(double[] re, double[] im, double[] re_inter, double[] im_inter, int start, int step, int num, int subArraySize) { + private static void fft_one_dim(double[] re, double[] im, double[] re_inter, double[] im_inter, int start, int step, int num, int subArraySize) { if(num == 1) return; double angle = -2*FastMath.PI/num; @@ -116,7 +150,7 @@ public static void fft_one_dim(double[] re, double[] im, double[] re_inter, doub } - public static void ifft_one_dim(double[] re, double[] im, double[] re_inter, double[] im_inter, int start, int step, int num, int subArraySize) { + private static void ifft_one_dim(double[] re, double[] im, double[] re_inter, double[] im_inter, int start, int step, int num, int subArraySize) { // conjugate input for (int i = start; i < start+num*step; i+=step){ @@ -134,14 +168,36 @@ public static void ifft_one_dim(double[] re, double[] im, double[] re_inter, dou } - private static boolean isPowerOfTwo(int n){ + /** + * Function for checking whether the given integer is a power of two. + * + * @param n integer to check + * @return true if n is a power of two, false otherwise + */ + public static boolean isPowerOfTwo(int n){ return (n != 0) && ((n & (n - 1)) == 0); } + /** + * Function to perform FFT on a given matrices. + * The given matrix only represents real values. + * The output contains one matrix for the real and one for the imaginary values. + * + * @param re matrix object representing the real values + * @return array of two matrix blocks + */ public static MatrixBlock[] fft(MatrixBlock re){ return fft(re, new MatrixBlock(re.getNumRows(),re.getNumColumns(), new double[re.getNumRows()*re.getNumColumns()])); } + /** + * Function to perform IFFT on a given matrices. + * The given matrix only represents real values. + * The output contains one matrix for the real and one for the imaginary values. + * + * @param re matrix object representing the real values + * @return array of two matrix blocks + */ public static MatrixBlock[] ifft(MatrixBlock re){ return ifft(re, new MatrixBlock(re.getNumRows(),re.getNumColumns(), new double[re.getNumRows()*re.getNumColumns()])); } diff --git a/src/test/java/org/apache/sysds/test/component/matrix/FourierTest.java b/src/test/java/org/apache/sysds/test/component/matrix/FourierTest.java index 41a8e48b571..bdd35f45206 100644 --- a/src/test/java/org/apache/sysds/test/component/matrix/FourierTest.java +++ b/src/test/java/org/apache/sysds/test/component/matrix/FourierTest.java @@ -22,7 +22,6 @@ import org.apache.sysds.runtime.matrix.data.MatrixBlock; import org.junit.Test; -import static org.apache.sysds.runtime.matrix.data.LibMatrixFourier.fft_one_dim; import static org.apache.sysds.runtime.matrix.data.LibMatrixFourier.fft; import static org.apache.sysds.runtime.matrix.data.LibMatrixFourier.ifft; @@ -34,42 +33,32 @@ public class FourierTest { @Test public void test_fft_one_dim() { - double[] re = {0, 18, -15, 3}; - double[] im = {0, 0, 0, 0}; - - double[] re_inter = new double[4]; - double[] im_inter = new double[4]; + MatrixBlock re = new MatrixBlock(1, 4, new double[]{0, 18, -15, 3}); + MatrixBlock im = new MatrixBlock(1, 4, new double[4]); double[] expected_re = {6, 15, -36, 15}; double[] expected_im = {0, -15, 0, 15}; - int cols = 4; - - fft_one_dim(re, im, re_inter, im_inter, 0, 1, cols, cols); + fft(re, im); - assertArrayEquals(expected_re, re, 0.0001); - assertArrayEquals(expected_im, im, 0.0001); + assertArrayEquals(expected_re, re.getDenseBlockValues(), 0.0001); + assertArrayEquals(expected_im, im.getDenseBlockValues(), 0.0001); } @Test public void test_fft_one_dim_2() { - double[] re = {0, 18, -15, 3, 5, 10, 5, 9}; - double[] im = new double[8]; - - double[] re_inter = new double[8]; - double[] im_inter = new double[8]; + MatrixBlock re = new MatrixBlock(1, 8, new double[]{0, 18, -15, 3, 5, 10, 5, 9}); + MatrixBlock im = new MatrixBlock(1, 8, new double[8]); double[] expected_re = {35, 4.89949, 15, -14.89949, -45, -14.89949, 15, 4.89949}; double[] expected_im = {0, 18.58579, -16, -21.41421, 0, 21.41421, 16, -18.58579}; - int cols = 8; - - fft_one_dim(re, im, re_inter, im_inter, 0, 1, cols, cols); + fft(re, im); - assertArrayEquals(expected_re, re, 0.0001); - assertArrayEquals(expected_im, im, 0.0001); + assertArrayEquals(expected_re, re.getDenseBlockValues(), 0.0001); + assertArrayEquals(expected_im, im.getDenseBlockValues(), 0.0001); } @Test From c79f0fd4d8434158ad6d151f4968e3584d4e4269 Mon Sep 17 00:00:00 2001 From: Jessica Priebe Date: Thu, 18 Jan 2024 14:42:12 +0100 Subject: [PATCH 040/133] updated stft branch and deleted from main --- .../runtime/matrix/data/LibMatrixSTFT.java | 112 ------------------ .../component/matrix/LibMatrixSTFTTest.java | 104 ---------------- 2 files changed, 216 deletions(-) delete mode 100644 src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixSTFT.java delete mode 100644 src/test/java/org/apache/sysds/test/component/matrix/LibMatrixSTFTTest.java diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixSTFT.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixSTFT.java deleted file mode 100644 index 01c61e5746f..00000000000 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixSTFT.java +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package org.apache.sysds.runtime.matrix.data; - -import static org.apache.sysds.runtime.matrix.data.LibMatrixFourier.fft; - -public class LibMatrixSTFT { - - public static MatrixBlock[] stft(MatrixBlock re, MatrixBlock im, int windowSize, int overlap) { - - int cols = re.getNumColumns(); - - double[][] in = new double[2][cols]; - in[0] = convertToArray(re); - in[1] = convertToArray(im); - - double[][] res = one_dim_stft(in[0], windowSize, overlap); - - return convertToMatrixBlocks(res); - } - - public static double[][] one_dim_stft(double[] signal, int windowSize, int overlap) { - - if (windowSize < 1 || (windowSize & (windowSize - 1)) != 0) { - throw new IllegalArgumentException("frameSize is not a power of two"); - } - int stepSize = windowSize - overlap; - int totalFrames = (signal.length - overlap) / stepSize; - - // Initialize the STFT output array: [time frame][frequency bin][real & imaginary parts] - double[][] stftOutput = new double[2][totalFrames * windowSize]; - - for (int frame = 0; frame < totalFrames; frame++) { - double[] windowedSignal = new double[windowSize]; - - // Apply window function (rectangular in this case) - for (int i = 0; i < windowSize; i++) { - int signalIndex = frame * stepSize + i; - windowedSignal[i] = (signalIndex < signal.length) ? signal[signalIndex] : 0; - } - - // Perform FFT on windowed signal - int l = windowedSignal.length; - - double[] tmp_re = new double[l]; - double[] tmp_im = new double[l]; - - for (int i = 0; i < l; i++) { - tmp_re[i] = windowedSignal[i]; - tmp_im[i] = 0; - } - - fft(tmp_re, tmp_im, 1, l); - - // Store the FFT result in the output array - int startIndex = windowSize * frame; - for (int i = 0; i < l; i++) { - stftOutput[0][startIndex + i] = tmp_re[i]; - stftOutput[1][startIndex + i] = tmp_im[i]; - } - //stftOutput[frame] = fftResult; - } - - return stftOutput; - } - - private static MatrixBlock[] convertToMatrixBlocks(double[][] in){ - - int cols = in[0].length; - - MatrixBlock re = new MatrixBlock(1, cols, in[0]); - MatrixBlock im = new MatrixBlock(1, cols, in[1]); - - return new MatrixBlock[]{re, im}; - } - - private static double[] convertToArray(MatrixBlock in){ - return in.getDenseBlockValues(); - } - - public static MatrixBlock[] stft(double[] in, int windowSize, int overlap){ - double[][] arr = new double[2][in.length]; - arr[0] = in; - return stft(convertToMatrixBlocks(arr), windowSize, overlap); - } - - public static MatrixBlock[] stft(double[][] in, int windowSize, int overlap){ - return stft(convertToMatrixBlocks(in), windowSize, overlap); - } - - public static MatrixBlock[] stft(MatrixBlock[] in, int windowSize, int overlap){ - return stft(in[0], in[1], windowSize, overlap); - } - -} diff --git a/src/test/java/org/apache/sysds/test/component/matrix/LibMatrixSTFTTest.java b/src/test/java/org/apache/sysds/test/component/matrix/LibMatrixSTFTTest.java deleted file mode 100644 index e2ed4a622c0..00000000000 --- a/src/test/java/org/apache/sysds/test/component/matrix/LibMatrixSTFTTest.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package org.apache.sysds.test.component.matrix; - -import org.apache.sysds.runtime.matrix.data.MatrixBlock; -import org.junit.Test; - -import static org.junit.Assert.assertArrayEquals; -import static org.apache.sysds.runtime.matrix.data.LibMatrixSTFT.stft; -import static org.apache.sysds.runtime.matrix.data.LibMatrixSTFT.one_dim_stft; - -public class LibMatrixSTFTTest { - - @Test - public void simple_test() { - - double[] signal = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}; - int windowSize = 4; - int overlap = 2; - double[][] stftResult = one_dim_stft(signal, windowSize, overlap); - - // 1st row real part, 2nd row imaginary part - double[][] expected = {{6, -2, -2, -2, 14, -2, -2, -2, 22, -2, -2, -2, 30, -2, -2, -2, 38, -2, -2, -2, 46, -2, -2, -2, 54, -2, -2, -2},{0, 2, 0, -2, 0, 2, 0, -2, 0, 2, 0, -2, 0, 2, 0, -2, 0, 2, 0, -2, 0, 2, 0, -2, 0, 2, 0, -2}}; - - for(double[] row : stftResult){ - for (double elem : row){ - System.out.print(elem + " "); - } - System.out.println(); - } - - assertArrayEquals(expected[0], stftResult[0], 0.0001); - assertArrayEquals(expected[1], stftResult[1], 0.0001); - - } - - @Test - public void matrix_block_one_dim_test(){ - - double[] in = {0, 18, -15, 3}; - - double[] expected_re = {6,15,-36,15}; - double[] expected_im = {0,-15,0,15}; - - MatrixBlock[] res = stft(in, 4, 0); - double[] res_re = res[0].getDenseBlockValues(); - double[] res_im = res[1].getDenseBlockValues(); - - for(double elem : res_re){ - System.out.print(elem+" "); - } - System.out.println(); - for(double elem : res_im){ - System.out.print(elem+" "); - } - - assertArrayEquals(expected_re, res_re, 0.0001); - assertArrayEquals(expected_im, res_im, 0.0001); - - } - - @Test - public void matrix_block_one_dim_test2(){ - - double[] in = {10, 5, -3, 8, 15, -6, 2, 0}; - - double[] expected_re = {20.0, 13.0, -6.0, 13.0, 14.0, -18.0, 10.0, -18.0, 11.0, 13.0, 23.0, 13.0}; - double[] expected_im = {0.0, 3.0, 0.0, -3.0, 0.0, -14.0, 0.0, 14.0, 0.0, 6.0, 0.0, -6.0 }; - - MatrixBlock[] res = stft(in, 4, 2); - double[] res_re = res[0].getDenseBlockValues(); - double[] res_im = res[1].getDenseBlockValues(); - - for(double elem : res_re){ - System.out.print(elem+" "); - } - System.out.println(); - for(double elem : res_im){ - System.out.print(elem+" "); - } - - assertArrayEquals(expected_re, res_re, 0.0001); - assertArrayEquals(expected_im, res_im, 0.0001); - - } - -} From 57b1125de42ad6135ced7c3f01b913c3776adaab Mon Sep 17 00:00:00 2001 From: Frederic Zoepffel Date: Fri, 19 Jan 2024 20:59:50 +0545 Subject: [PATCH 041/133] adjusted tests with files --- .../test/component/matrix/FourierTestData.py | 214 --- .../component/matrix/FourierTestLarge.java | 1520 +++++++++++++++++ .../matrix/FourierTestWithFiles.java | 453 ----- 3 files changed, 1520 insertions(+), 667 deletions(-) delete mode 100644 src/test/java/org/apache/sysds/test/component/matrix/FourierTestData.py create mode 100644 src/test/java/org/apache/sysds/test/component/matrix/FourierTestLarge.java delete mode 100644 src/test/java/org/apache/sysds/test/component/matrix/FourierTestWithFiles.java diff --git a/src/test/java/org/apache/sysds/test/component/matrix/FourierTestData.py b/src/test/java/org/apache/sysds/test/component/matrix/FourierTestData.py deleted file mode 100644 index 371b837f6bb..00000000000 --- a/src/test/java/org/apache/sysds/test/component/matrix/FourierTestData.py +++ /dev/null @@ -1,214 +0,0 @@ -""" -Licensed to the Apache Software Foundation (ASF) under one -or more contributor license agreements. See the NOTICE file -distributed with this work for additional information -regarding copyright ownership. The ASF licenses this file -to you under the Apache License, Version 2.0 (the -"License"); you may not use this file except in compliance -with the License. You may obtain a copy of the License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, -software distributed under the License is distributed on an -"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY -KIND, either express or implied. See the License for the -specific language governing permissions and limitations -under the License. -""" - -import numpy as np -import csv -import time - -def generate_inputs(num_inputs, max_power): - for _ in range(num_inputs): - power = np.random.randint(1, max_power+1) - length = 2 ** power - yield np.random.rand(length) # generate array of random floats - -def generate_complex_inputs(num_inputs, max_power): - for _ in range(num_inputs): - power = np.random.randint(1, max_power+1) - length = 2 ** power - real_part = np.random.rand(length) - imag_part = np.random.rand(length) - complex_array = real_part + 1j * imag_part - yield complex_array - - -def compute_fft(inputs): - total_time = 0 - num_calculations = 0 - - for input_array in inputs: - start_time = time.time() - result = np.fft.fft(input_array) - end_time = time.time() - - total_time += end_time - start_time - num_calculations += 1 - - if num_calculations % 1000 == 0: - average_time = total_time / num_calculations - print(f"Average execution time after {num_calculations} calculations: {average_time:.6f} seconds") - - yield result - -def compute_ifft(inputs): - total_time = 0 - num_calculations = 0 - - for input_array in inputs: - start_time = time.time() - result = np.fft.ifft(input_array) - end_time = time.time() - - total_time += end_time - start_time - num_calculations += 1 - - if num_calculations % 1000 == 0: - average_time = total_time / num_calculations - print(f"Average execution time after {num_calculations} calculations: {average_time:.6f} seconds") - - yield result - - - -def save_to_file(inputs, outputs, filename, mode='a'): - with open(filename, mode, newline='') as file: - writer = csv.writer(file) - for input_array, output_array in zip(inputs, outputs): - flattened_data = np.concatenate((input_array, output_array.real, output_array.imag)) - writer.writerow(flattened_data) - - -def save_to_file_complex(inputs, outputs, filename, mode='a'): - with open(filename, mode, newline='') as file: - writer = csv.writer(file) - for input_array, output_array in zip(inputs, outputs): - flattened_data = np.concatenate((input_array.real, input_array.imag, output_array.real, output_array.imag)) - writer.writerow(flattened_data) - -def generate_complex_inputs_2d(num_inputs, max_power): - for _ in range(num_inputs): - power = np.random.randint(1, max_power+1) - rows = 2 ** power - cols = 2 ** power - real_part = np.random.rand(rows, cols) - imag_part = np.random.rand(rows, cols) - complex_array = real_part + 1j * imag_part - yield complex_array - -def compute_fft_2d(inputs): - - total_time = 0 - num_calculations = 0 - - for input_array in inputs: - start_time = time.time() - result = np.fft.fft2(input_array) - end_time = time.time() - - total_time += end_time - start_time - num_calculations += 1 - - if num_calculations % 1000 == 0: - average_time = total_time / num_calculations - print(f"Average execution time after {num_calculations} calculations: {average_time:.6f} seconds") - - yield result - -def compute_ifft_2d(inputs): - - total_time = 0 - num_calculations = 0 - - for input_array in inputs: - start_time = time.time() - result = np.fft.ifft2(input_array) - end_time = time.time() - - total_time += end_time - start_time - num_calculations += 1 - - if num_calculations % 1000 == 0: - average_time = total_time / num_calculations - print(f"Average execution time after {num_calculations} calculations: {average_time:.6f} seconds") - - yield result - -def save_to_file_complex_2d(inputs, outputs, filename, mode='a'): - with open(filename, mode, newline='') as file: - writer = csv.writer(file) - for input_array, output_array in zip(inputs, outputs): - flattened_input = np.concatenate((input_array.real.flatten(), input_array.imag.flatten())) - flattened_output = np.concatenate((output_array.real.flatten(), output_array.imag.flatten())) - writer.writerow(np.concatenate((flattened_input, flattened_output))) - - -# Parameters -num_inputs = 100000 -batch_size = 10000 -max_power = 10 # example max power of 2 for input length - - -# Process and save in batches -for i in range(0, num_inputs, batch_size): - current_batch = min(batch_size, num_inputs - i) - inputs = list(generate_inputs(current_batch, max_power)) - outputs = list(compute_fft(inputs)) - save_to_file(inputs, outputs, "fft_data.csv", mode='a' if i > 0 else 'w') - print(f"Batch {i//batch_size + 1} out of {num_inputs//batch_size} saved to fft_data.csv") - -print("All data for fft processed and saved.") - -# Process and save in batches - - -for i in range(0, num_inputs, batch_size): - current_batch = min(batch_size, num_inputs - i) - inputs = list(generate_inputs(current_batch, max_power)) - outputs = list(compute_ifft(inputs)) - save_to_file(inputs, outputs, "ifft_data.csv", mode='a' if i > 0 else 'w') - print(f"Batch {i//batch_size + 1} out of {num_inputs//batch_size} saved to ifft_data.csv") - -print("All real iff data processed and saved.") - -# Process and save in batches -for i in range(0, num_inputs, batch_size): - current_batch = min(batch_size, num_inputs - i) - inputs = list(generate_complex_inputs(current_batch, max_power)) - outputs = list(compute_ifft(inputs)) - save_to_file_complex(inputs, outputs, "complex_ifft_data.csv", mode='a' if i > 0 else 'w') - print(f"Batch {i//batch_size + 1} out of {num_inputs//batch_size} saved to complex_ifft_data.csv") - -print("All complex ifft data processed and saved.") - - -num_inputs = 100000 -batch_size = 1000 -max_power = 3 - -# Process and save 2D FFT data in batches -filename_2d = "complex_fft_2d_data.csv" -for i in range(0, num_inputs, batch_size): - current_batch = min(batch_size, num_inputs - i) - inputs_2d = list(generate_complex_inputs_2d(current_batch, max_power)) - outputs_2d = list(compute_fft_2d(inputs_2d)) - save_to_file_complex_2d(inputs_2d, outputs_2d, filename_2d, mode='a' if i > 0 else 'w') - print(f"Batch {i//batch_size + 1} out of {num_inputs//batch_size} saved to {filename_2d}") - -print("All complex 2D fft data processed and saved.") - -# Process and save 2D IFFT data in batches -filename_2d = "complex_ifft_2d_data.csv" -for i in range(0, num_inputs, batch_size): - current_batch = min(batch_size, num_inputs - i) - inputs_2d = list(generate_complex_inputs_2d(current_batch, max_power)) - outputs_2d = list(compute_ifft_2d(inputs_2d)) - save_to_file_complex_2d(inputs_2d, outputs_2d, filename_2d, mode='a' if i > 0 else 'w') - print(f"Batch {i//batch_size + 1} out of {num_inputs//batch_size} saved to {filename_2d}") - -print("All complex 2D ifft data processed and saved.") - diff --git a/src/test/java/org/apache/sysds/test/component/matrix/FourierTestLarge.java b/src/test/java/org/apache/sysds/test/component/matrix/FourierTestLarge.java new file mode 100644 index 00000000000..cc011453afb --- /dev/null +++ b/src/test/java/org/apache/sysds/test/component/matrix/FourierTestLarge.java @@ -0,0 +1,1520 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.sysds.test.component.matrix; + +import org.junit.Test; + +import static org.junit.Assert.assertTrue; +import static org.apache.sysds.runtime.matrix.data.LibMatrixFourier.fft; +import static org.apache.sysds.runtime.matrix.data.LibMatrixFourier.ifft; + +public class FourierTestLarge { + int progressInterval = 5000; + + // prior to executing the following tests it is necessary to run the Numpy + // Script in FourierTestData.py + // and add the generated files to the root of the project. + @Test + public void testFftWithGeneratedData() { + // Define the input data array (first 1000 inputs) + double[][] inputData = { + { 0.307856129173271, 0.08921910198690264, 0.618009230694374, 0.5313115890139828, 0.8321948342164646, + 0.5931468404266381, 0.6183643824098176, 0.09098483235809818, 0.020002539689180643, + 0.9179524283672298, 0.6092688826975122, 0.03765800262802799, 0.6583959135030817, + 0.346267450443292, 0.9794472087243126, 0.8642963614250007 }, + { 0.997301378000895, 0.3502640931966453, 0.1954636101454681, 0.10391072327500839, 0.21327031067780433, + 0.0643272237899376, 0.8221952489149892, 0.9534348411569122, 0.9534997101452614, + 0.5141779472770125, 0.2306040802116438, 0.8386736679248497, 0.0981318516277605, + 0.8074595469184841, 0.254078430810886, 0.5996117331901718 }, + { 0.870957838868506, 0.912745725021149, 0.5345683140982496, 0.18114400014042686, 0.5931978577526508, + 0.14192088683195558, 0.9208741124169134, 0.6525015591512732 }, + { 0.29788916579478153, 0.27748600931023504, 0.4853091738976063, 0.8567045012601394 }, + { 0.6352092395258702, 0.43145575925660073 }, + { 0.5617699291201627, 0.21489241033852313 }, + { 0.6576030646834683, 0.9290768001953902, 0.09848637471541899, 0.06425572022630976, 0.5396213600686569, + 0.22085716679561374, 0.7272917506111063, 0.14096681648120335, 0.003323239715782522, + 0.29724749937056516, 0.2356003703231878, 0.4343299880624373, 0.6875247706424916, + 0.8485613871894802, 0.9773614895293514, 0.48945201661534776 }, + { 0.8821994027742829, 0.21163278841593125, 0.4932536463151129, 0.48650972337895804, 0.7786999918312242, + 0.14154217886198583, 0.050381939456006886, 0.4817754647240189, 0.25197827244265536, + 0.19196634342654173, 0.2248835944599762, 0.09610110952097772, 0.9234776641814892, + 0.8345039754950259, 0.9145560074968945, 0.2448292011827906 }, + { 0.7385591035373894, 0.6934429707180507, 0.20469771060726105, 0.7146443181329221 }, + { 0.09423365095676517, 0.4200805363713518, 0.8898189099184024, 0.15185704349763007, 0.976019182222216, + 0.26121755769518185, 0.07688163464324083, 0.5605512821290499, 0.01968958370279128, + 0.02586318786500097, 0.13903676787373986, 0.29289492175451814, 0.9598461594746724, + 0.06685602573191851, 0.8132935172237855, 0.07525352129151597 }, + { 0.06574341421208751, 0.8492596587119035, 0.8065274870296378, 0.27175430100069153, 0.2954082423553953, + 0.3563055835463892, 0.5417770631268415, 0.9615039909442821 }, + { 0.04211428054995747, 0.7911133121974274, 0.2118116815925455, 0.483908183679128, 0.9084187853511388, + 0.6796408581806436, 0.10941522676794624, 0.7455635937529194, 0.620135811235268, + 0.7769721780197776, 0.7167767032200226, 0.10390503304337428, 0.6407001194530335, + 0.26083464261776423, 0.5806294628171111, 0.2518664967318035 }, + { 0.18129008646882727, 0.8669649844463896, 0.028246543418389947, 0.48363667274187305, + 0.7943230293132295, 0.6961876877536417, 0.4275685366852513, 0.45634721662560607, + 0.1005692992795224, 0.9050284283882628, 0.3146718845340458, 0.7861414010588036, + 0.4770427042197599, 0.1462914941041602, 0.3965644055730563, 0.9153636362926348 }, + { 0.8161580934614122, 0.18477962701579598 }, + { 0.08028451743536968, 0.24496797132982906, 0.0849486774071061, 0.7881678651794348, 0.32208849264834005, + 0.027946650916384486, 0.6481634237831947, 0.07243767209378071, 0.5807489567390403, + 0.852034426396901, 0.7611516595374496, 0.05624561395049921, 0.2909447900344724, + 0.02896220325285881, 0.6042269314873742, 0.709088439526459 }, + { 0.9419390726681351, 0.25416475350108325, 0.3668196423318234, 0.0004937686334369751, + 0.45115568384401916, 0.15272066875461188, 0.13006769168951093, 0.008318934989370197, + 0.7881465440955732, 0.933662847575916, 0.08386521463152519, 0.6529529591688511, + 0.4473354453449845, 0.31327384554825755, 0.5058970453705052, 0.870525057224827 }, + { 0.5505180216822709, 0.5781501773859546 }, + { 0.8956572090779427, 0.8299050830853423, 0.1424103295292285, 0.13728455802847184, 0.8000171582078991, + 0.6298805318267743, 0.46391319943080855, 0.5663723332416153 }, + { 0.9683653719909785, 0.2232641902635809, 0.6148539423093378, 0.37453351154356274 }, + { 0.7067035754354158, 0.0663739473919116 }, + { 0.47086229838998894, 0.8960946381686666, 0.9533858690069733, 0.5176638484634724, 0.015341863574319992, + 0.09419290215121512, 0.7068849515138174, 0.2050445924658565 }, + { 0.5785763844543972, 0.9176187041034242 }, + { 0.5241184105276374, 0.2509183778867127, 0.12853860707933074, 0.9590386361137108 }, + { 0.8571553248185413, 0.6263814942317649, 0.95881731622584, 0.310415053009538, 0.4775273408541796, + 0.6329378690509789, 0.7178046354589865, 0.9923541273411732, 0.44017806754392763, + 0.2522486894328567, 0.12849993855560982, 0.8988211375767242, 0.3617603937666176, + 0.10711880543199137, 0.9905379056406248, 0.8906739530932947 }, + { 0.8536820346534935, 0.5656301408899768, 0.24077798818075857, 0.34595328041358364, 0.8821071518415022, + 0.5357200569496784, 0.13935877170910727, 0.2502969398391611 }, + { 0.7293440752321705, 0.335697322075603, 0.7609624135786428, 0.5430882990642741, 0.7903417394181252, + 0.09592791445258453, 0.8251861573566924, 0.5242152290578941, 0.5274752317660253, + 0.5978730300121011, 0.9257445772312801, 0.23083171339790576, 0.4992817006893182, + 0.7009970366772414, 0.09991780231136771, 0.19590332619221507 }, + { 0.45683639103550133, 0.4127698308179104, 0.15849785349035195, 0.23820398589917613, + 0.42556319726902625, 0.3902556783233726, 0.44922620996988205, 0.47889491731179334, + 0.31088942492181826, 0.8561445618860377, 0.35456306768933543, 0.6656974246709467, + 0.60674113513703, 0.4713576352952902, 0.7389009344705372, 0.8211538788821755 }, + { 0.18081479839397807, 0.47100279012993196 }, + { 0.21954540433093261, 0.40458389194582534, 0.7208723075287345, 0.8846816526712216, 0.5327808546064456, + 0.8864372849612085, 0.05751443204835982, 0.807453849418354, 0.9593743129540142, + 0.6715092386060035, 0.7003192281171006, 0.5476351764742082, 0.7413425202579201, + 0.3598546138493517, 0.1710023879149254, 0.3477614210012976 }, + { 0.04105479173235693, 0.2790129805889898 }, + { 0.31270679654880107, 0.6563226858676515, 0.065309659982147, 0.9594045059676773, 0.41461631741634075, + 0.8549726106226234, 0.6885625924540151, 0.34077133669639903 }, + { 0.05216212273283238, 0.1663159578169714, 0.6755000798755829, 0.8410660871499066 }, + { 0.43067498044817587, 0.38256389857433204 }, + { 0.637327693590928, 0.3133763054769817, 0.016610252725152153, 0.32652177024153395, + 0.009691976865415008, 0.23600780862854676, 0.8836055793679196, 0.45244777988456386 }, + { 0.5906578759363037, 0.7944662801327856, 0.17863645183223031, 0.32277739351179, 0.3231823947395689, + 0.4034735202642745, 0.06399390314662634, 0.17540386741349312 }, + { 0.17815921306628235, 0.8585869087579457 }, + { 0.5871751868409761, 0.6534562814682419 }, + { 0.39586773608713244, 0.7387586667130125, 0.19184209782072326, 0.10410270481271588, 0.9951764576402166, + 0.23388910739629953, 0.7647794115198386, 0.8052705154780063 }, + { 0.6585162649365032, 0.711175495841072, 0.4676580282401064, 0.8826289448730669, 0.82861818848699, + 0.9426098006296768, 0.9615519675050708, 0.40172361372445387 }, + { 0.554938210317202, 0.45311686056607847, 0.848278396952335, 0.9663886131569552, 0.993175945037519, + 0.4321940233579258, 0.9786877966394255, 0.22383267497846604 }, + { 0.14832468791702524, 0.7911386648581192, 0.4342135601328583, 0.39064350292654393, 0.00154990539545663, + 0.7056390559656973, 0.5449016159778458, 0.44995063726730145 }, + { 0.6787570562911824, 0.2108141544985952, 0.36959074400723846, 0.6065094525663848, 0.14370437302298023, + 0.7471705670569604, 0.3991987649234662, 0.6377650280189965, 0.7966311569652879, + 0.6699688376083757, 0.11046232705364989, 0.0343797538976689, 0.4263281825639861, + 0.06018487827135577, 0.27543446279089245, 0.6307832714073219 }, + { 0.007401290917390502, 0.8640389112554114, 0.08779720334707941, 0.6587228816523323, + 0.21985520800643954, 0.4963969113562372, 0.25041837572771375, 0.2996763341721056 }, + { 0.9283706112012573, 0.9008656544411213, 0.01039871402142889, 0.637525445174257 }, + { 0.838047562953646, 0.9020667732919446 }, + { 0.7455441724472716, 0.5020772412030806, 0.10951344204683389, 0.30933274344662487 }, + { 0.8180656768575455, 0.8666279680604059, 0.42583580021283085, 0.1433193988956274, 0.8669332518159855, + 0.7414686722465978, 0.5939337651104747, 0.6789552509532184 }, + { 0.48808916431556837, 0.8241477106539906, 0.7262098103482119, 0.899834357237205, 0.8834046134657804, + 0.6029189784057519, 0.46118570354302213, 0.20904657865994403, 0.17528071980731652, + 0.9766278629190644, 0.2643652212919221, 0.22466069704047253, 0.5868640515110155, + 0.40572150873462176, 0.012091970132293572, 0.7702657347835162 }, + { 0.8296574984897637, 0.2009561060794306, 0.4599409596490742, 0.2981967624462212 }, + { 0.9517083930235997, 0.9818426792525043 } + }; + + double[][][] outputData = { + { { 8.114375727757187, 0.5925913961852104, -0.8416569218183314, -0.22284951774343884, + -1.0066402879440184, 0.27554774792062964, -1.483807235895858, 0.5061247315739602, + 1.1727025144588428, 0.5061247315739602, -1.483807235895858, 0.27554774792062964, + -1.0066402879440184, -0.22284951774343884, -0.8416569218183314, 0.5925913961852104 }, + { 0.0, 0.00425759907320819, 0.5957854277534866, 2.186428829721094, -0.4223350357989528, + 1.3405434768573832, -0.14528152773100134, -0.14643207093697122, 0.0, + 0.14643207093697122, 0.14528152773100134, -1.3405434768573832, 0.4223350357989528, + -2.186428829721094, -0.5957854277534866, -0.00425759907320819 } }, + { { 7.9964043972637295, -0.85788941138668, 2.065867366646952, 0.2645073319462676, 0.759861880368734, + 0.6762306422958229, 1.2129304850342315, 0.09235810856712429, -0.46731515619431363, + 0.09235810856712429, 1.2129304850342315, 0.6762306422958229, 0.759861880368734, + 0.2645073319462676, 2.065867366646952, -0.85788941138668 }, + { 0.0, 0.8007124150209053, 1.0870614472850983, -1.0027520373092829, 0.7594021543648628, + -0.47928657559005056, -0.21335053145242822, 1.784731712940313, 0.0, -1.784731712940313, + 0.21335053145242822, 0.47928657559005056, -0.7594021543648628, 1.0027520373092829, + -1.0870614472850983, -0.8007124150209053 } }, + { { 4.8079102942811245, 1.156115577646565, 0.008713270105993765, -0.6005956154148545, + 1.0312859519915147, -0.6005956154148545, 0.008713270105993765, 1.156115577646565 }, + { 0.0, 0.17455045446816958, -0.2210210525614047, -0.5980611421691582, 0.0, 0.5980611421691582, + 0.2210210525614047, -0.17455045446816958 } }, + { { 1.9173888502627623, -0.18742000810282478, -0.3509921708779866, -0.18742000810282478 }, + { 0.0, 0.5792184919499044, 0.0, -0.5792184919499044 } }, + { { 1.0666649987824708, 0.20375348026926943 }, { 0.0, 0.0 } }, + { { 0.7766623394586858, 0.3468775187816395 }, { 0.0, 0.0 } }, + { { 7.35155981522581, 1.7384348144213493, -0.36205061306792863, 0.7115391307931807, -0.1506675500686654, + 0.4372769750045813, -0.7703890395558666, -0.27013162034836824, 0.5020650253531165, + -0.27013162034836824, -0.7703890395558666, 0.4372769750045813, -0.1506675500686654, + 0.7115391307931807, -0.36205061306792863, 1.7384348144213493 }, + { 0.0, 1.2350796850853472, 1.3528374749311034, -0.5177322782271997, -1.166738312165751, + -0.7694859455764832, -1.3882955152725982, 0.3917123754407248, 0.0, -0.3917123754407248, + 1.3882955152725982, 0.7694859455764832, 1.166738312165751, 0.5177322782271997, + -1.3528374749311034, -1.2350796850853472 } }, + { { 7.208291303963872, 1.6448982875247267, -0.8709621365176501, -1.2546616319678878, 1.1532801435016609, + 0.9134446382882846, -0.26503782507390033, 1.2172032274813867, 1.8305697339514122, + 1.2172032274813867, -0.26503782507390033, 0.9134446382882846, 1.1532801435016609, + -1.2546616319678878, -0.8709621365176501, 1.6448982875247267 }, + { 0.0, 0.7473959934264138, 0.7534008934360946, -0.07634193189820554, -0.07042978739273931, + -0.6293807074039233, 0.25979948108047013, -0.3847534714803642, 0.0, 0.3847534714803642, + -0.25979948108047013, 0.6293807074039233, 0.07042978739273931, 0.07634193189820554, + -0.7534008934360946, -0.7473959934264138 } }, + { { 2.3513441029956232, 0.5338613929301284, -0.4648304747063223, 0.5338613929301284 }, + { 0.0, 0.02120134741487134, 0.0, -0.02120134741487134 } }, + { { 5.823393482351781, 0.9136499207248283, -1.603500573737872, -0.7020472269919936, 0.13075774669727602, + -1.2520745980052537, -2.040383640336792, 1.3386481732883144, 2.1142453296794463, + 1.3386481732883144, -2.040383640336792, -1.2520745980052537, 0.13075774669727602, + -0.7020472269919936, -1.603500573737872, 0.9136499207248283 }, + { 0.0, -0.4121748237807462, -0.08693254435692731, -0.7861482143975829, 0.3065394610092609, + -0.7981716440636941, 0.19042850749330453, -0.35950616245668343, 0.0, + 0.35950616245668343, -0.19042850749330453, 0.7981716440636941, -0.3065394610092609, + 0.7861482143975829, 0.08693254435692731, 0.4121748237807462 } }, + { { 4.1482797409272285, 0.6066330243002018, -0.9871528935889964, -1.0659626805868174, + -0.7293673274793042, -1.0659626805868174, -0.9871528935889964, 0.6066330243002018 }, + { 0.0, -0.12559491018544305, 0.02769304968668118, 0.40390593762014965, 0.0, + -0.40390593762014965, -0.02769304968668118, 0.12559491018544305 } }, + { { 7.923806369209862, -1.0597881688755275, -0.15343866443479803, -0.701824885719718, + 0.5927359221917725, -0.40648735694530763, -1.620298961603096, -0.14398571120068893, + -0.2638022272358147, -0.14398571120068893, -1.620298961603096, -0.40648735694530763, + 0.5927359221917725, -0.701824885719718, -0.15343866443479803, -1.0597881688755275 }, + { 0.0, -0.5098005943518916, -0.3926881055602594, 0.7944914010859094, -0.9233176838083876, + -1.1214718762239086, 0.08439928489476206, -1.3548892080692885, 0.0, 1.3548892080692885, + -0.08439928489476206, 1.1214718762239086, 0.9233176838083876, -0.7944914010859094, + 0.3926881055602594, 0.5098005943518916 } }, + { { 7.9762380109034545, -0.08102561731855723, -0.2601631725122897, 1.2537848144634045, + 0.38617374907059565, -0.6434301753632343, -1.7188495230569896, -0.2064458730243935, + -2.53568503191929, -0.2064458730243935, -1.7188495230569896, -0.6434301753632343, + 0.38617374907059565, 1.2537848144634045, -0.2601631725122897, -0.08102561731855723 }, + { 0.0, -0.17500593841444329, -0.10397394228803791, 1.0518049218488463, 0.02701633202646292, + 0.05602413235726633, -1.0664029708997815, 0.0983345724678551, 0.0, -0.0983345724678551, + 1.0664029708997815, -0.05602413235726633, -0.02701633202646292, -1.0518049218488463, + 0.10397394228803791, 0.17500593841444329 } }, + { { 1.000937720477208, 0.6313784664456162 }, { 0.0, 0.0 } }, + { { 6.152408291718494, -0.701864450013982, 0.7389893051923667, -0.6570737426544427, -0.8244239353579019, + 0.6745758755808154, -0.6429889222091717, -1.3174954401270726, 0.5927066064261997, + -1.3174954401270726, -0.6429889222091717, 0.6745758755808154, -0.8244239353579019, + -0.6570737426544427, 0.7389893051923667, -0.701864450013982 }, + { 0.0, 0.2166164561807843, -0.37363526340890696, 1.9069743954629836, 0.47202833885420037, + 0.9505271452772353, -1.1862153000609332, -0.6152559835494934, 0.0, 0.6152559835494934, + 1.1862153000609332, -0.9505271452772353, -0.47202833885420037, -1.9069743954629836, + 0.37363526340890696, -0.2166164561807843 } }, + { { 6.9013391753724305, 0.6001789336263573, 1.5013874922440678, 0.21234328277453474, 1.5419271519293472, + -0.8364191839302667, 0.1618014829053418, 0.6390670818196226, 0.5291135045797226, + 0.6390670818196226, 0.1618014829053418, -0.8364191839302667, 1.5419271519293472, + 0.21234328277453474, 1.5013874922440678, 0.6001789336263573 }, + { 0.0, 1.402962398507333, -0.1657532563195791, 1.1827153358128617, -0.12153139536338342, + 1.0437298788884946, -0.5363130165129142, 1.2792578955791045, 0.0, -1.2792578955791045, + 0.5363130165129142, -1.0437298788884946, 0.12153139536338342, -1.1827153358128617, + 0.1657532563195791, -1.402962398507333 } }, + { { 1.1286681990682257, -0.027632155703683714 }, { 0.0, 0.0 } }, + { { 4.465440402428082, 0.5404896430462359, 1.0893508383258046, -0.3492095413061488, 0.1385553900636749, + -0.3492095413061488, 1.0893508383258046, 0.5404896430462359 }, + { 0.0, 0.48347502888031313, -0.7561287236420293, -0.15953071092284696, 0.0, 0.15953071092284696, + 0.7561287236420293, -0.48347502888031313 } }, + { { 2.18101701610746, 0.35351142968164073, 0.9854216124931725, 0.35351142968164073 }, + { 0.0, 0.15126932127998183, 0.0, -0.15126932127998183 } }, + { { 0.7730775228273274, 0.6403296280435042 }, { 0.0, 0.0 } }, + { { 3.85947096373431, 0.8014953943534662, -1.1740666585564816, 0.10954547527787173, 0.4334790012358889, + 0.10954547527787173, -1.1740666585564816, 0.8014953943534662 }, + { 0.0, -1.034586268721768, -0.2675790993905529, -0.5415844337354562, 0.0, 0.5415844337354562, + 0.2675790993905529, 1.034586268721768 } }, + { { 1.4961950885578212, -0.339042319649027 }, { 0.0, 0.0 } }, + { { 1.8626140316073916, 0.3955798034483067, -0.5572999963934553, 0.3955798034483067 }, + { 0.0, 0.7081202582269981, 0.0, -0.7081202582269981 } }, + { { 9.64323205203265, 1.0222697551724542, 1.0324747401969991, 0.77067558102107, -0.6590386688977952, + -1.496670252648384, -0.11638342471365593, 1.3716339455533146, 0.2213297936960057, + 1.3716339455533146, -0.11638342471365593, -1.496670252648384, -0.6590386688977952, + 0.77067558102107, 1.0324747401969991, 1.0222697551724542 }, + { 0.0, -0.634301326905181, 0.9994818327031652, -0.7420494450419806, 1.473577412873138, + -0.18504033224304028, -0.24256873993315792, 0.3857755742440071, 0.0, + -0.3857755742440071, 0.24256873993315792, 0.18504033224304028, -1.473577412873138, + 0.7420494450419806, -0.9994818327031652, 0.634301326905181 } }, + { { 3.8135263644772612, -0.07491474109162899, 1.35565242660513, 0.01806450671561144, 0.4183255282924616, + 0.01806450671561144, 1.35565242660513, -0.07491474109162899 }, + { 0.0, -0.1902080867353592, -0.5050999775869104, 0.012630346207943427, 0.0, + -0.012630346207943427, 0.5050999775869104, 0.1902080867353592 } }, + { { 8.382787568513443, -0.6219859812329366, 0.025775381293649605, -0.2422386710017057, + -0.06536820337234373, 1.9046978726738621, -0.09138364751214496, -0.23299784657463904, + 1.933719826653804, -0.23299784657463904, -0.09138364751214496, 1.9046978726738621, + -0.06536820337234373, -0.2422386710017057, 0.025775381293649605, -0.6219859812329366 }, + { 0.0, -0.4421693421141475, -0.8962692960095064, -0.27841993134928456, -0.23645673550524116, + -0.06789283541651772, 0.6269367662742193, 0.9325979087338476, 0.0, -0.9325979087338476, + -0.6269367662742193, 0.06789283541651772, 0.23645673550524116, 0.27841993134928456, + 0.8962692960095064, 0.4421693421141475 } }, + { { 7.835696127070186, -0.013838185418746013, 0.3035453412810106, 0.36108371712932497, + 0.098842082743269, -0.20157362393768677, -0.8327023741784841, 0.4381159566818401, + -0.8332596991032202, 0.4381159566818401, -0.8327023741784841, -0.20157362393768677, + 0.098842082743269, 0.36108371712932497, 0.3035453412810106, -0.013838185418746013 }, + { 0.0, 1.2951778169532924, 0.667179370978872, 0.6934919236313549, 0.07342250044148102, + 0.36890779027205756, -0.6829530755425919, 0.24588193212198017, 0.0, + -0.24588193212198017, 0.6829530755425919, -0.36890779027205756, -0.07342250044148102, + -0.6934919236313549, -0.667179370978872, -1.2951778169532924 } }, + { { 0.65181758852391, -0.2901879917359539 }, { 0.0, 0.0 } }, + { { 9.012668576685904, -1.3888872356007793, -0.4114927520171256, -0.937566206657709, 0.8033347365401919, + -0.7316542605934145, 0.2210854368582879, 0.09879206835957632, -0.807165681169038, + 0.09879206835957632, 0.2210854368582879, -0.7316542605934145, 0.8033347365401919, + -0.937566206657709, -0.4114927520171256, -1.3888872356007793 }, + { 0.0, -0.5973815143710736, -1.2682664037495632, 0.009556220704126317, 0.26514707020269235, + 0.29524978930704304, 1.1170830276155366, -1.1459346083740547, 0.0, 1.1459346083740547, + -1.1170830276155366, -0.29524978930704304, -0.26514707020269235, -0.009556220704126317, + 1.2682664037495632, 0.5973815143710736 } }, + { { 0.32006777232134676, -0.2379581888566329 }, { 0.0, 0.0 } }, + { { 4.292666505555655, -0.679815938802624, -0.02654913847102025, 0.47599689706754467, + -1.3302757727530472, 0.47599689706754467, -0.02654913847102025, -0.679815938802624 }, + { 0.0, 0.3262799322896599, -0.21111945382619868, -0.9202259326540763, 0.0, 0.9202259326540763, + 0.21111945382619868, -0.3262799322896599 } }, + { { 1.7350442475752934, -0.6233379571427505, -0.2797198423584627, -0.6233379571427505 }, + { 0.0, 0.6747501293329352, 0.0, -0.6747501293329352 } }, + { { 0.8132388790225079, 0.04811108187384383 }, { 0.0, 0.0 } }, + { { 2.875589166781041, 0.7713866408436004, -0.25319616163672876, 0.48388479260742556, + 0.2188818383177884, 0.48388479260742556, -0.25319616163672876, 0.7713866408436004 }, + { 0.0, 0.9013306732173781, 0.22958543602056936, -0.8326599800681568, 0.0, 0.8326599800681568, + -0.22958543602056936, -0.9013306732173781 } }, + { { 2.8525916869770724, 0.43974029342312404, 0.6712099156970159, 0.09521066897034558, + -0.539650435667614, 0.09521066897034558, 0.6712099156970159, 0.43974029342312404 }, + { 0.0, -0.49532500025494997, -0.6997585394717769, -0.266039902883742, 0.0, 0.266039902883742, + 0.6997585394717769, 0.49532500025494997 } }, + { { 1.036746121824228, -0.6804276956916634 }, { 0.0, 0.0 } }, + { { 1.240631468309218, -0.06628109462726572 }, { 0.0, 0.0 } }, + { { 4.229686697467945, 0.25348848112557965, 0.4344226843867871, -1.452105924231748, 0.46564470866787655, + -1.452105924231748, 0.4344226843867871, 0.25348848112557965 }, + { 0.0, 0.7117411383627559, -0.06327455381858982, -0.4341334890354749, 0.0, 0.4341334890354749, + 0.06327455381858982, -0.7117411383627559 } }, + { { 5.8544823042369405, -0.6738021106296501, 0.057924457678316, 0.3335982635286765, + -0.02179340589959944, 0.3335982635286765, 0.057924457678316, -0.6738021106296501 }, + { 0.0, 0.3174912848162346, -0.36943273787322806, -0.6702965937136942, 0.0, 0.6702965937136942, + 0.36943273787322806, -0.3174912848162346 } }, + { { 5.450612521005906, -0.9485093939451186, -0.2788520382370394, 0.07203392450448454, + 1.2995481768870563, 0.07203392450448454, -0.2788520382370394, -0.9485093939451186 }, + { 0.0, -0.4094516196808049, 0.304910404211417, -0.670270419054986, 0.0, 0.670270419054986, + -0.304910404211417, 0.4094516196808049 } }, + { { 3.4663616304408476, 0.249168612623289, -0.8292405827982222, 0.04438095241984823, + -1.2083820915944758, 0.04438095241984823, -0.8292405827982222, 0.249168612623289 }, + { 0.0, 0.09216717947344956, -0.6561835806299711, -0.12920893221652552, 0.0, 0.12920893221652552, + 0.6561835806299711, -0.09216717947344956 } }, + { { 6.797683010944343, -0.49676461769680663, 1.4010987748295984, -0.2858605784410965, + 0.8907344700681898, -0.14132138994562243, 0.4096125405094099, 0.4524501833871039, + -0.3974688757069753, 0.4524501833871039, 0.4096125405094099, -0.14132138994562243, + 0.8907344700681898, -0.2858605784410965, 1.4010987748295984, -0.49676461769680663 }, + { 0.0, -0.978354076595978, 0.5864810413074062, 0.3462260624882627, 0.22129906845508507, + 1.4529657578358368, 0.19732072800046563, -1.0021096194124273, 0.0, 1.0021096194124273, + -0.19732072800046563, -1.4529657578358368, -0.22129906845508507, -0.3462260624882627, + -0.5864810413074062, 0.978354076595978 } }, + { { 2.8843071164347096, -0.20637601439624492, -0.11095908015096312, -0.21853181978185315, + -1.753362960437463, -0.21853181978185315, -0.11095908015096312, -0.20637601439624492 }, + { 0.0, -0.35122522728194183, -0.40203660678721076, -0.6764675720432105, 0.0, 0.6764675720432105, + 0.40203660678721076, 0.35122522728194183 } }, + { { 2.4771604248380648, 0.9179718971798284, -0.5996217743926923, 0.9179718971798284 }, + { 0.0, -0.2633402092668643, 0.0, 0.2633402092668643 } }, + { { 1.7401143362455906, -0.0640192103382986 }, { 0.0, 0.0 } }, + { { 1.666467599143811, 0.6360307304004377, 0.04364762984439996, 0.6360307304004377 }, + { 0.0, -0.19274449775645575, 0.0, 0.19274449775645575 } }, + { { 5.1351397841526865, 0.41838515507659374, 0.6652293633502253, -0.5161203049934737, + 0.2743972038409872, -0.5161203049934737, 0.6652293633502253, 0.41838515507659374 }, + { 0.0, 0.4583487213357241, -0.7858219904581578, 0.1221527915404364, 0.0, -0.1221527915404364, + 0.7858219904581578, -0.4583487213357241 } }, + { { 8.510714682849697, 0.882363829984205, -0.34943439017669387, 0.018617453499083503, 0.669785843784231, + 0.588967002531606, -1.2643631715311279, -0.2387145079818872, -1.3157321740194359, + -0.2387145079818872, -1.2643631715311279, 0.588967002531606, 0.669785843784231, + 0.018617453499083503, -0.34943439017669387, 0.882363829984205 }, + { 0.0, -1.4735161576417974, -1.1800811497522834, 0.6456240585574142, -0.7056086929922909, + 1.3408042647661378, -0.14548643382264692, 0.40782629638598544, 0.0, + -0.40782629638598544, 0.14548643382264692, -1.3408042647661378, 0.7056086929922909, + -0.6456240585574142, 1.1800811497522834, 1.4735161576417974 } }, + { { 1.7887513266644897, 0.36971653884068945, 0.790445589613186, 0.36971653884068945 }, + { 0.0, 0.09724065636679058, 0.0, -0.09724065636679058 } }, + { { 1.933551072276104, -0.030134286228904683 }, { 0.0, 0.0 } } + }; + + long totalTime = 0; // Total time for all FFT computations + int numCalculations = 0; // Number of FFT computations + + for (int i = 0; i < inputData.length; i++) { + double[] re = inputData[i]; + double[] im = new double[re.length]; // Initialize with zeros + + double[][] expected = outputData[i]; + + long startTime = System.nanoTime(); + fft(re, im, 1, re.length); + long endTime = System.nanoTime(); + + totalTime += (endTime - startTime); + numCalculations++; + + double[][] actual = { re, im }; + + // Validate the FFT results + validateFftResults(expected, actual, i + 1); + } + + double averageTime = (totalTime / 1e6) / numCalculations; // Average time in milliseconds + System.out.println("Average execution time: " + String.format("%.8f", averageTime / 1000) + " s"); + } + + private void validateFftResults(double[][] expected, double[][] actual, int lineNumber) { + int length = expected[0].length; + for (int i = 0; i < length; i++) { + assertEquals("Mismatch in real part at index " + i + " in line " + lineNumber, + expected[0][i], actual[0][i], 1e-9); + assertEquals("Mismatch in imaginary part at index " + i + " in line " + lineNumber, + expected[1][i], actual[1][i], 1e-9); + } + } + + @Test + public void testIfftWithRealNumpyData() { + + // Define the input data array (real IFFT inputs) + double[][] inputData = { + { 0.5398705320215192, 0.1793355360736929, 0.9065254044489506, 0.45004385530909075, 0.11128090341119468, + 0.11742862805303522, 0.7574827475752481, 0.2778193170985158, 0.9251562928110273, + 0.9414429667551927, 0.45131569795507087, 0.9522067687409731, 0.22491032260636257, + 0.6579426733967295, 0.7021558730366062, 0.7861117825617701, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0 }, + { 0.6557180567930387, 0.7738395851954255, 0.5282681528342636, 0.6867068152297491, 0.032829567791427205, + 0.060095237489160236, 0.5250841288480065, 0.9356398294818203, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0.15785491881056868, 0.9056824935624028, 0.8375304868692421, 0.9188982121338262, 0, 0, 0, 0 }, + { 0.2362087514654716, 0.031154253034164303, 0.6157257211463447, 0.3773680629310894, 0, 0, 0, 0 }, + { 0.3751160998031162, 0.4253486725063852, 0.3535750705572219, 0.9557962271336204, 0, 0, 0, 0 }, + { 0.38741575855731003, 0.9967628087038197, 0.6755232743231949, 0.381124935406706, 0, 0, 0, 0 }, + { 0.7952407554893315, 0.5188862746399061, 0.4828070990424008, 0.41146079690881665, 0.5523554474649665, + 0.3371675982794765, 0.9086513990497248, 0.9480377757565142, 0.29262673918949955, + 0.5029584519842616, 0.1673523322593311, 0.909533552169132, 0.15086390797253646, + 0.08903568696591746, 0.9762713649580028, 0.79327533892987, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0 }, + { 0.16166982209302416, 0.48331281957071315, 0.8297038690562623, 0.2481405411628217, 0.37652293557756644, + 0.35568361617736255, 0.9886708014513632, 0.029556032119152365, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0.822115700110782, 0.29037305812947556, 0, 0 }, + { 0.1364777724494669, 0.4413020994649117, 0, 0 }, + { 0.4260028251864256, 0.2655565566693795, 0, 0 }, + { 0.8162743187254883, 0.6131080055615629, 0.016530179658639677, 0.3647951810730393, 0, 0, 0, 0 }, + { 0.32404367744317786, 0.9975863008660675, 0, 0 }, + { 0.9287511285137915, 0.9885317245671525, 0.29542406948511, 0.307667914659569, 0, 0, 0, 0 }, + { 0.6391321634628706, 0.8633641189940532, 0.4309541791982252, 0.9557087449945955, 0.3185873920535448, + 0.16533643465056203, 0.6461385746878021, 0.7043234939129138, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0.9801750501829359, 0.25789696071187607, 0, 0 }, + { 0.22972137572909201, 0.5817904901405937, 0, 0 }, + { 0.7239381644147941, 0.7687362814620392, 0.0873707955450509, 0.9506567262739939, 0, 0, 0, 0 }, + { 0.6141463720447145, 0.7403205748478789, 0.44227773948538585, 0.7277081432710838, 0.9352081899539255, + 0.4240443645856953, 0.4346954941173261, 0.5879657145765288, 0.23766574473359614, + 0.6530152865673962, 0.11484140648508312, 0.14574807816162283, 0.9624874452393961, + 0.5592097861022578, 0.005521469527721035, 0.06956740990618893, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0 }, + { 0.3838845675941237, 0.5088807724736699, 0, 0 }, + { 0.6983109361802508, 0.6244339329560505, 0.9008871130564603, 0.8932736022416019, 0.8727581351265782, + 0.9856438893255001, 0.5696671959058417, 0.15020089983612606, 0.058348906817813906, + 0.7629908745245777, 0.48442101642797164, 0.8502536763232881, 0.14290448979940018, + 0.14311515061344016, 0.933512771559466, 0.7512746845582662, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0 }, + { 0.7556811638466671, 0.7893033739069542, 0.3459619060619262, 0.19402945604163735, 0, 0, 0, 0 }, + { 0.14095847557513064, 0.9354102907913319, 0.47692666603219047, 0.9655253211517809, 0, 0, 0, 0 }, + { 0.5132699837151409, 0.48338858084587877, 0.21082789930932544, 0.38487660081876207, + 0.49257322976621676, 0.4546677266078989, 0.8125433122155187, 0.1871279228630126, + 0.9935603064559684, 0.14244380671166657, 0.33692793538062416, 0.8184163712132382, + 0.1909607709342982, 0.40792019329451423, 0.745094091124709, 0.5706291972106916, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0.13418006969295326, 0.4945716623889531, 0.11760243909921375, 0.2570864674258849, 0.07735057384397426, + 0.019114456711389338, 0.9417433018717989, 0.9498903789723119, 0.15850190266259456, + 0.2328071188590526, 0.25865880162909693, 0.008950504888770583, 0.8898672604984333, + 0.6296228316964232, 0.769087346766234, 0.595502278059397, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0 }, + { 0.6547029168282242, 0.4498064503056928, 0.5566645171023767, 0.8447764906827644, 0.8134333284372349, + 0.033684622580624546, 0.2968711640065672, 0.8285143785250315, 0.5791098493622432, + 0.5979371998171618, 0.3650614808824525, 0.8518268346228536, 0.8198089679249101, + 0.9819781459749414, 0.9725187053011243, 0.17296496382033655, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0 }, + { 0.6448779915504477, 0.09112098044117456, 0.9263097426435519, 0.27812390544808974, 0.2760810901241125, + 0.7646817729525881, 0.911491047258462, 0.4005509961726136, 0.9694912938404336, + 0.8886893035007664, 0.6133994082666177, 0.3533649879581323, 0.24573475689499646, + 0.8300954022435542, 0.010790385307265948, 0.3607772174510355, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0 }, + { 0.9753223707638033, 0.6956234223864403, 0.5023331089283625, 0.6507908760350136, 0.10653272730910857, + 0.4996211399988053, 0.8760128351901267, 0.602426660576998, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0.6504577559004796, 0.25145420305800725, 0.493505599054606, 0.4515689832358094, 0.14356101422612022, + 0.3341029115381504, 0.5008369992482713, 0.25927336770416753, 0.9144019406085643, + 0.269776237000278, 0.6382189705979336, 0.9554129674543379, 0.6887405590404585, + 0.5039371147315215, 0.8252078215260031, 0.10805307033951939, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0 }, + { 0.41724967962818993, 0.2781633024698039, 0, 0 }, + { 0.4002455779217782, 0.564838604931205, 0.1953327862932963, 0.7012116008559239, 0.8022033779885992, + 0.2808573734656151, 0.3000946668377774, 0.17230898600893108, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0.6461124772516227, 0.48674094781923605, 0.10036448207277027, 0.015471823700216714, + 0.7689797229739221, 0.03476124705509731, 0.8330787816937497, 0.35390071912245424, + 0.18458690293633118, 0.42355207544691875, 0.5798363871357116, 0.4243527922467508, + 0.2879869168805991, 0.13736279699966614, 0.26166696186760385, 0.9787373283247385, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0.1398260006661618, 0.12153200522616103, 0.7121966216410146, 0.9133849754561364, 0.7314612511926722, + 0.739267240037694, 0.7546027553155675, 0.342420614369761, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0.22574862111952854, 0.5690849507265368, 0.631317280107177, 0.8629733195605777, 0.3899681673812473, + 0.8794702418181954, 0.1517842815507917, 0.1657877194968821, 0.8083699897670112, + 0.4355578268421948, 0.1969236012819674, 0.06184299476225663, 0.07860704151155451, + 0.397292331637211, 0.3839838104251525, 0.26205058162224937, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0 }, + { 0.33135924721579535, 0.5366302683143123, 0.25432093749208906, 0.7498876975281418, 0, 0, 0, 0 }, + { 0.6134308716246728, 0.09706486959071292, 0.356584692623391, 0.9766274230749799, 0.24172086288049444, + 0.7974589582847552, 0.7896787540010787, 0.5664999509048404, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0.18439523648303413, 0.5938145715696269, 0.5138768707589609, 0.03744323769215385, 0.9335962783171388, + 0.5763983101382048, 0.2806801388811724, 0.40858218250589795, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0.6665104076212911, 0.12712939621992436, 0, 0 }, + { 0.07848088184489588, 0.9037683971776713, 0, 0 }, + { 0.2961816890284832, 0.6548352902037954, 0.004297273252101497, 0.6379597126860397, 0.394953301386181, + 0.3591579315172063, 0.5200829702509112, 0.7348916802637337, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0.15333899851758237, 0.9557388547562318, 0, 0 }, + { 0.24490652495244614, 0.764767557933974, 0.7412612945684217, 0.8345076232268165, 0.9713064054627377, + 0.1666699320547278, 0.6385013568574381, 0.02837645523199317, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0.43636393937714724, 0.5006457049751213, 0.29183886078443877, 0.7771528617739111, 0.9760896495636339, + 0.4211692751284558, 0.46136604887195976, 0.4385215837142291, 0.6480247707507675, + 0.2869034885339521, 0.21139535714638436, 0.623852932281303, 0.10723939013625783, + 0.7423978700202447, 0.8339592164587588, 0.6541139999860581, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0 }, + { 0.673783082139485, 0.08489915452638419, 0.4428704894374401, 0.6322989775504414, 0, 0, 0, 0 }, + { 0.435204198844909, 0.60467095443427, 0.43492988438024205, 0.45115625691681205, 0.6073353909030185, + 0.7354160659126683, 0.6584642868836427, 0.49841087464599554, 0.3218054140407358, + 0.8547287810979488, 0.25920067933895863, 0.3577532915633288, 0.17696516490870162, + 0.9810705645133889, 0.08037125000861489, 0.9406431999485564, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0 }, + { 0.14276827960992966, 0.13654559645650455, 0, 0 }, + { 0.10449615216668384, 0.8809636163201752, 0.23757224642400443, 0.6033844272609862, 0.3180497932853712, + 0.8000641846428788, 0.36691139380180937, 0.9936551508162524, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0.5619461607033829, 0.05587776081117257, 0.8242650361208828, 0.31679799606054226, 0.67922806899631, + 0.31896202698295617, 0.47844249679108297, 0.43194377662374206, 0, 0, 0, 0, 0, 0, 0, 0 }, + { 0.5928888719853808, 0.8700201519901183, 0.909292241027492, 0.1017088272754374, 0.7250571447087572, + 0.3172141314203841, 0.05139597560789155, 0.8469852045420578, 0.744706896124228, + 0.24878503090173887, 0.3248378441207508, 0.6159251794660624, 0.46869738424898, + 0.42094147190658915, 0.2078392223975123, 0.17713383848172415, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0 }, + { 0.8259324464059233, 0.9375338155913502, 0.5557276593014934, 0.017649722866079798, 0, 0, 0, 0 } + }; + + double[][][] outputData = { + { { 0.5613143313659362, -0.020146500453061336, 0.07086545895481336, -0.05003801442765281, + -0.06351635451036074, -0.03346768844048936, 0.07023899089706032, 0.007330763123826495, + 0.016022890367311193, 0.007330763123826495, 0.07023899089706032, -0.03346768844048936, + -0.06351635451036074, -0.05003801442765281, 0.07086545895481336, -0.020146500453061336 }, + { 0.0, -0.07513090216687965, 0.023854392878864396, -0.018752997939582024, -0.035626994964481226, + -0.07808216015046443, 0.036579082654843526, -0.10605270957897009, 0.0, + 0.10605270957897009, -0.036579082654843526, 0.07808216015046443, 0.035626994964481226, + 0.018752997939582024, -0.023854392878864396, 0.07513090216687965 } }, + { { 0.5247726717078615, 0.16295052246714098, -0.04560058213722554, -0.007228400216738096, + -0.08929769514117739, -0.007228400216738096, -0.04560058213722554, 0.16295052246714098 }, + { 0.0, 0.04148190873050992, -0.09855147775337295, 0.04068590273394563, 0.0, + -0.04068590273394563, 0.09855147775337295, -0.04148190873050992 } }, + { { 0.70499152784401, -0.16991889201466837, -0.20729882500410457, -0.16991889201466837 }, + { 0.0, -0.003303929642855835, 0.0, 0.003303929642855835 } }, + { { 0.31511419714426747, -0.09487924242021828, 0.11085303916164066, -0.09487924242021828 }, + { 0.0, -0.08655345247423127, 0.0, 0.08655345247423127 } }, + { { 0.527459017500086, 0.0053852573114735736, -0.16311343231991685, 0.0053852573114735736 }, + { 0.0, -0.1326118886568088, 0.0, 0.1326118886568088 } }, + { { 0.6102066942477578, -0.07202687894147122, -0.07873717780750517, -0.07202687894147122 }, + { 0.0, 0.15390946832427843, 0.0, -0.15390946832427843 } }, + { { 0.5522827825662304, 0.02247888349994264, 0.06894003068860452, 0.054250799183907905, + -0.04649970907457038, -0.025283389794838417, -0.02085901328343852, 0.07420721118594584, + -0.011511651888006347, 0.07420721118594584, -0.02085901328343852, -0.025283389794838417, + -0.04649970907457038, 0.054250799183907905, 0.06894003068860452, 0.02247888349994264 }, + { 0.0, 0.025696394507123577, -0.06942446748581371, 0.0016937241252907506, -0.10089121574342319, + 0.029974458022771634, 0.08492094910243575, -0.04639575646850304, 0.0, + 0.04639575646850304, -0.08492094910243575, -0.029974458022771634, 0.10089121574342319, + -0.0016937241252907506, 0.06942446748581371, -0.025696394507123577 } }, + { { 0.43415755465103323, -0.034896028361847214, -0.16002273910462933, -0.018817250009288352, + 0.1549843023935208, -0.018817250009288352, -0.16002273910462933, -0.034896028361847214 }, + { 0.0, 0.010730391426110649, 0.0701624828082627, 0.05047212452488589, 0.0, -0.05047212452488589, + -0.0701624828082627, -0.010730391426110649 } }, + { { 0.5562443791201288, 0.26587132099065325 }, { 0.0, 0.0 } }, + { { 0.2888899359571893, -0.1524121635077224 }, { 0.0, 0.0 } }, + { { 0.34577969092790256, 0.08022313425852307 }, { 0.0, 0.0 } }, + { { 0.4526769212546825, 0.19993603476671215, -0.03627467206261856, 0.19993603476671215 }, + { 0.0, 0.062078206122130886, 0.0, -0.062078206122130886 } }, + { { 0.6608149891546227, -0.3367713117114448 }, { 0.0, 0.0 } }, + { { 0.6300937093064057, 0.15833176475717037, -0.01800611030695498, 0.15833176475717037 }, + { 0.0, 0.1702159524768959, 0.0, -0.1702159524768959 } }, + { { 0.5904431377443209, 0.07954608309180142, -0.014921649796201489, 0.0005901097605300332, + -0.08174006039371018, 0.0005901097605300332, -0.014921649796201489, 0.07954608309180142 }, + { 0.0, 0.057018991161973565, -0.07891646065786176, 0.11081509003436779, 0.0, + -0.11081509003436779, 0.07891646065786176, -0.057018991161973565 } }, + { { 0.619036005447406, 0.36113904473552993 }, { 0.0, 0.0 } }, + { { 0.40575593293484286, -0.17603455720575084 }, { 0.0, 0.0 } }, + { { 0.6326754919239695, 0.1591418422174358, -0.22702101194404703, 0.1591418422174358 }, + { 0.0, -0.04548011120298867, 0.0, 0.04548011120298867 } }, + { { 0.4784014512253625, 0.011293452743340458, -0.056787040374204935, -0.023693104723780656, + 0.10951072764725725, 0.07974560959080311, -0.07394839942767145, 0.02677419921741667, + -0.010045968526969012, 0.02677419921741667, -0.07394839942767145, 0.07974560959080311, + 0.10951072764725725, -0.023693104723780656, -0.056787040374204935, 0.011293452743340458 }, + { 0.0, 0.07201888847998587, 0.03497215168058931, 0.05943123839853866, 0.05285004163673773, + -0.01085420783312048, 0.02035937888991158, 0.008553256069694384, 0.0, + -0.008553256069694384, -0.02035937888991158, 0.01085420783312048, -0.05285004163673773, + -0.05943123839853866, -0.03497215168058931, -0.07201888847998587 } }, + { { 0.4463826700338968, -0.06249810243977311 }, { 0.0, 0.0 } }, + { { 0.6138748297032897, 0.08206729827609008, -0.04196993291849642, 0.06274035723249816, + -0.06976035181410603, -0.05171556292590567, 0.00959458517750721, 0.06689841475792666, + -0.03127350909406679, 0.06689841475792666, 0.00959458517750721, -0.05171556292590567, + -0.06976035181410603, 0.06274035723249816, -0.04196993291849642, 0.08206729827609008 }, + { 0.0, 0.08138486461332381, 0.04127830896039093, -0.10717873814123505, -0.008051188471232124, + -0.02059807337669975, 0.056012288708000416, -0.0144978819539354, 0.0, + 0.0144978819539354, -0.056012288708000416, 0.02059807337669975, 0.008051188471232124, + 0.10717873814123505, -0.04127830896039093, -0.08138486461332381 } }, + { { 0.5212439749642962, 0.10242981444618521, 0.029577559990000446, 0.10242981444618521 }, + { 0.0, 0.1488184794663292, 0.0, -0.1488184794663292 } }, + { { 0.6297051883876085, -0.08399204761426496, -0.32076261758394786, -0.08399204761426496 }, + { 0.0, -0.007528757590112262, 0.0, 0.007528757590112262 } }, + { { 0.48407674552921653, -0.008227964517310823, 0.0213027140552919, 0.023595673719326214, + 0.005310690802590445, -0.06652446503281143, 0.08160932212853239, -0.06891582485441083, + 0.052892945583508655, -0.06891582485441083, 0.08160932212853239, -0.06652446503281143, + 0.005310690802590445, 0.023595673719326214, 0.0213027140552919, -0.008227964517310823 }, + { 0.0, -0.007093439306719466, -0.05389072430458301, -0.014648934153181905, + -0.029539361540359133, 0.028236671825160584, 0.07234447177670174, -0.03961094803635662, + 0.0, 0.03961094803635662, -0.07234447177670174, -0.028236671825160584, + 0.029539361540359133, 0.014648934153181905, 0.05389072430458301, + 0.007093439306719466 } }, + { { 0.40840858719165507, -0.00019587417138387797, 0.01785707194350384, -0.03945144723928065, + -0.05169950516677427, 0.06413973151079717, -0.1021740546918613, -0.03057286834254297, + 0.009965374816382294, -0.03057286834254297, -0.1021740546918613, 0.06413973151079717, + -0.05169950516677427, -0.03945144723928065, 0.01785707194350384, -0.00019587417138387797 }, + { 0.0, -0.05557309041453939, -0.13647515970329846, 0.09642410678380406, -0.027207097480659137, + -0.007933514822127903, 0.030346016285416798, 0.04319845964314343, 0.0, + -0.04319845964314343, -0.030346016285416798, 0.007933514822127903, 0.027207097480659137, + -0.09642410678380406, 0.13647515970329846, 0.05557309041453939 } }, + { { 0.6137287510109087, 0.019157896750848162, -0.05426698771160002, -0.107174755578878, + 0.04224619970375573, 0.039969043468570706, 0.004338296440140308, 0.06694608222595438, + 0.018542615219732883, 0.06694608222595438, 0.004338296440140308, 0.039969043468570706, + 0.04224619970375573, -0.107174755578878, -0.05426698771160002, 0.019157896750848162 }, + { 0.0, -0.06481804503251337, 0.01040922623102531, 0.03115577981125027, -0.03966726556078537, + 0.07314271886708576, 0.05386721014638309, -0.021237196104759097, 0.0, + 0.021237196104759097, -0.05386721014638309, -0.07314271886708576, 0.03966726556078537, + -0.03115577981125027, -0.01040922623102531, 0.06481804503251337 } }, + { { 0.5353487676283651, -0.0948504913692811, 0.04684477769694442, -0.013771271402122677, + -0.0203628406916192, 0.02514842443382726, 0.08972440209952712, 0.0023200127650800234, + 0.039423196857370835, 0.0023200127650800234, 0.08972440209952712, 0.02514842443382726, + -0.0203628406916192, -0.013771271402122677, 0.04684477769694442, -0.0948504913692811 }, + { 0.0, 0.02928471698851646, 0.005673137221436961, 0.0113450994077995, 0.07386064700676326, + -0.0921306795918339, -0.07150532757161823, -0.08177764531839596, 0.0, + 0.08177764531839596, 0.07150532757161823, 0.0921306795918339, -0.07386064700676326, + -0.0113450994077995, -0.005673137221436961, -0.02928471698851646 } }, + { { 0.6135828926485822, 0.12164819021773775, -0.037061355755697134, 0.09554922064593595, + 0.0014673678992679906, 0.09554922064593595, -0.037061355755697134, 0.12164819021773775 }, + { 0.0, -0.02511081481753711, -0.00724662177834573, 0.06830911674790396, 0.0, + -0.06830911674790396, 0.00724662177834573, 0.02511081481753711 } }, + { { 0.4992818447040142, -0.026335242639876785, -0.014162971662674932, -0.009204839208816325, + -0.0037880075406994396, -0.039667809092817426, 0.10573273706798308, 0.009221844764489362, + 0.10758448782129032, 0.009221844764489362, 0.10573273706798308, -0.039667809092817426, + -0.0037880075406994396, -0.009204839208816325, -0.014162971662674932, -0.026335242639876785 }, + { 0.0, -0.09052573027874725, 0.019800562825848633, 0.037129649810409254, -0.02593987015036732, + 0.010443783508403973, 0.04409059421606548, 0.019083289622832046, 0.0, + -0.019083289622832046, -0.04409059421606548, -0.010443783508403973, 0.02593987015036732, + -0.037129649810409254, -0.019800562825848633, 0.09052573027874725 } }, + { { 0.3477064910489969, 0.069543188579193 }, { 0.0, 0.0 } }, + { { 0.4271366217878908, -0.07189292138917516, 0.08837768784741297, -0.028596528627530097, + -0.002667519527527973, -0.028596528627530097, 0.08837768784741297, -0.07189292138917516 }, + { 0.0, 0.05875422493751868, -0.0034780760585043646, 0.08494469507363896, 0.0, + -0.08494469507363896, 0.0034780760585043646, -0.05875422493751868 } }, + { { 0.4073432727204618, 0.014805197956703195, 0.05793821643397497, 0.10942961383389802, + 0.0070449629545399906, 0.04114695896896992, -0.08622162389229589, -0.05000037718074825, + 0.05048330638107701, -0.05000037718074825, -0.08622162389229589, 0.04114695896896992, + 0.0070449629545399906, 0.10942961383389802, 0.05793821643397497, 0.014805197956703195 }, + { 0.0, -0.008842335656083382, -0.032743438452449085, -0.046196276979382764, + -0.04312784975457762, 0.005801406633435733, 0.019074670841659862, -0.07709285356659565, + 0.0, 0.07709285356659565, -0.019074670841659862, -0.005801406633435733, + 0.04312784975457762, 0.046196276979382764, 0.032743438452449085, + 0.008842335656083382 } }, + { { 0.5568364329881461, -0.17902159944745377, -0.07443901563721851, 0.031112786815826174, + 0.02768522421570796, 0.031112786815826174, -0.07443901563721851, -0.17902159944745377 }, + { 0.0, -0.009434766955953945, -0.04937579307025532, 0.0011667664626842864, 0.0, + -0.0011667664626842864, 0.04937579307025532, 0.009434766955953945 } }, + { { 0.4062976724756584, 0.013942954902943575, 0.0013568203618884583, -0.07879442082783945, + 0.008667802900890809, -0.05295232243525326, 0.0693361048873288, -0.027851553801721537, + -0.047959823332604595, -0.027851553801721537, 0.0693361048873288, -0.05295232243525326, + 0.008667802900890809, -0.07879442082783945, 0.0013568203618884583, 0.013942954902943575 }, + { 0.0, 0.10338853801320178, 0.02821697248022432, -0.03906633642088774, 0.05804692097388578, + -0.018017802505488005, -0.008342126196425707, 0.04659679046117832, 0.0, + -0.04659679046117832, 0.008342126196425707, 0.018017802505488005, -0.05804692097388578, + 0.03906633642088774, -0.02821697248022432, -0.10338853801320178 } }, + { { 0.4680495376375846, 0.019259577430926572, -0.17520944528364238, 0.019259577430926572 }, + { 0.0, -0.05331435730345738, 0.0, 0.05331435730345738 } }, + { { 0.5548832978731156, -0.05169341469959422, -0.0363889640149128, 0.14462091688563883, + -0.054529502590706436, 0.14462091688563883, -0.0363889640149128, -0.05169341469959422 }, + { 0.0, -0.07979294428422681, -0.081075443263044, 0.028480571060195102, 0.0, + -0.028480571060195102, 0.081075443263044, 0.07979294428422681 } }, + { { 0.44109835329327374, -0.059306377579101247, 0.04042931314500495, -0.12799388287942493, + 0.03703877781680284, -0.12799388287942493, 0.04042931314500495, -0.059306377579101247 }, + { 0.0, -0.0021153720251691766, 0.09052343268872248, -0.060414554994616315, 0.0, + 0.060414554994616315, -0.09052343268872248, 0.0021153720251691766 } }, + { { 0.39681990192060773, 0.26969050570068337 }, { 0.0, 0.0 } }, + { { 0.4911246395112836, -0.4126437576663877 }, { 0.0, 0.0 } }, + { { 0.4502949810735565, 0.022355638075107313, 0.020844343363956436, -0.04704854116453177, + -0.14641617259413728, -0.04704854116453177, 0.020844343363956436, 0.022355638075107313 }, + { 0.0, -0.04690643540201719, -0.044857271403596466, 0.08203998884768524, 0.0, + -0.08203998884768524, 0.044857271403596466, 0.04690643540201719 } }, + { { 0.5545389266369071, -0.4011999281193247 }, { 0.0, 0.0 } }, + { { 0.5487871437860694, -0.10918772610680917, -0.0204437151263345, -0.07241224402076372, + 0.10020675167419157, -0.07241224402076372, -0.0204437151263345, -0.10918772610680917 }, + { 0.0, 0.1369624550245931, 0.008569176441236517, 0.11127247059684721, 0.0, -0.11127247059684721, + -0.008569176441236517, -0.1369624550245931 } }, + { { 0.525689684343914, 0.04293325970025039, -0.030179739992632723, -0.05038212963216292, + 0.02307239166039156, -0.0161186370831989, 0.0303121987961356, -0.029347700828293622, + -0.02990503020774543, -0.029347700828293622, 0.0303121987961356, -0.0161186370831989, + 0.02307239166039156, -0.05038212963216292, -0.030179739992632723, 0.04293325970025039 }, + { 0.0, 0.031650959576365614, -0.05249532649964127, -0.06330481947587874, -0.03390781494360795, + 0.07112408901498432, 0.04651605442534566, -0.05113269678961534, 0.0, + 0.05113269678961534, -0.04651605442534566, -0.07112408901498432, 0.03390781494360795, + 0.06330481947587874, 0.05249532649964127, -0.031650959576365614 } }, + { { 0.45846292591343774, 0.057728148175511224, 0.09986385987502491, 0.057728148175511224 }, + { 0.0, -0.1368499557560143, 0.0, 0.1368499557560143 } }, + { { 0.524882891146362, 0.008511396928025477, 0.014781289867711371, 0.009887920266107053, + 0.006771504255369168, 0.039851202083645135, -0.018192657733470778, -0.029900823076734365, + -0.15309835748275913, -0.029900823076734365, -0.018192657733470778, 0.039851202083645135, + 0.006771504255369168, 0.009887920266107053, 0.014781289867711371, 0.008511396928025477 }, + { 0.0, 0.03486331238468826, -0.04200452046561043, -0.029916623097555238, 0.05799517143022398, + -0.04274944723179936, -0.03641639881897832, -0.08556206824813507, 0.0, + 0.08556206824813507, 0.03641639881897832, 0.04274944723179936, -0.05799517143022398, + 0.029916623097555238, 0.04200452046561043, -0.03486331238468826 } }, + { { 0.1396569380332171, 0.0031113415767125563 }, { 0.0, 0.0 } }, + { { 0.5381371205897701, 0.014951746342371994, -0.022742211846719845, -0.06834015662204383, + -0.281379724170303, -0.06834015662204383, -0.022742211846719845, 0.014951746342371994 }, + { 0.0, -0.043512210721145014, 0.010498527860726914, -0.011177423876693775, 0.0, + 0.011177423876693775, -0.010498527860726914, 0.043512210721145014 } }, + { { 0.458432915386259, -0.027736276833153158, -0.007691662901534085, -0.001584200240078635, + 0.17753752526665573, -0.001584200240078635, -0.007691662901534085, -0.027736276833153158 }, + { 0.0, 0.009796688554373531, -0.04673774811126945, -0.07665894627807641, 0.0, + 0.07665894627807641, 0.04673774811126945, -0.009796688554373531 } }, + { { 0.476464338512819, 0.010629551641227537, 0.03935742410033851, -0.01969212546385932, + 0.06487406336960619, -0.0647718460563506, -0.02137726920635457, 0.03587991384427056, + 0.02662510901480497, 0.03587991384427056, -0.02137726920635457, -0.0647718460563506, + 0.06487406336960619, -0.01969212546385932, 0.03935742410033851, 0.010629551641227537 }, + { 0.0, 0.030136326600295898, 0.06420857116384811, 0.09222343873200076, 0.0072004835283467905, + 0.08643721045556424, -0.05765328972900675, -0.039739841791084904, 0.0, + 0.039739841791084904, 0.05765328972900675, -0.08643721045556424, -0.0072004835283467905, + -0.09222343873200076, -0.06420857116384811, -0.030136326600295898 } }, + { { 0.5842109110412117, 0.06755119677610749, 0.10661914181249668, 0.06755119677610749 }, + { 0.0, 0.2299710231813176, 0.0, -0.2299710231813176 } } + }; + + for (int i = 0; i < inputData.length; i++) { + double[] re = inputData[i]; // Real part of input + double[] im = new double[re.length]; // Imaginary part of input (all zeros for real IFFT) + + double[][] expected = outputData[i]; // Expected output + + ifft(re, im, 1, re.length); // Perform IFFT + + double[][] actual = new double[][] { re, im }; + // Validate the IFFT results + validateFftResults(expected, actual, i + 1); + } + } + + @Test + public void testIfftWithComplexNumpyData() { + + // Define the input data array (complex IFFT inputs) + double[][] inputData = { + { 0.17768499045697306, 0.3405035491673728, 0.9272906450602005, 0.28247504052271843, 0.42775517613102865, + 0.8338783039818357, 0.9813749624557385, 0.47224489612381737, 0.7936831995784907, + 0.5584182145651306, 0.5296113722056018, 0.6687593295928902, 0.9630598447622862, + 0.7130539473424196, 0.860081483892192, 0.8985058305053549 }, + { 0.5574117454125004, 0.5153534964811869, 0.2218520797445649, 0.6278331124271054, 0.5264697956167971, + 0.9311289722152635, 0.13031360760348354, 0.6303629910772452 }, + { 0.8630369856754253, 0.9497763465868887, 0.9997691100440745, 0.3303206858594411, 0.9418293674995452, + 0.836702533317602, 0.42360226191376127, 0.28470444389038796, 0.18653359615821063, + 0.6164549770372061, 0.22800778967348623, 0.8045503278555484, 0.7141418111594348, + 0.8898346433405011, 0.5648709181981051, 0.7063388262940075, 0.33464462295280606, + 0.5338979798606405, 0.3604790551977437, 0.4551044306632025, 0.023102489774489032, + 0.5939484248343428, 0.601633008560747, 0.5056641001742173, 0.4945185946051195, + 0.3632887663114238, 0.09711935047598264, 0.8610061039014398, 0.1695426000726007, + 0.5420844999569931, 0.22423519306858575, 0.8138053316534443 }, + { 0.2668982957216445, 0.6518643198476277, 0.38293047888504705, 0.5276109887708621, 0.800891602234696, + 0.665489651348857, 0.5102919050998651, 0.03294208149274114 }, + { 0.8673593019099284, 0.5449051089951431, 0.35927077618823344, 0.9657806185363754 }, + { 0.3275052635434117, 0.13324953722224242, 0.9813054584386846, 0.41940627067946346, 0.5854578390799309, + 0.9370081210453547, 0.4965403182131879, 0.40441779810528955, 0.5902823763509569, + 0.2816096727771966, 0.9187065056993756, 0.01750173716395509, 0.9430939103428495, + 0.524796393731078, 0.6522622245916397, 0.7191101906183052, 0.924218305299526, + 0.41557703109579636, 0.27205034406713446, 0.9913812499805844, 0.8474192033613039, + 0.7090188824891868, 0.6353901388073759, 0.9455732252453851, 0.5575374113914232, + 0.8165674671938592, 0.9677824877594866, 0.4330735440317255, 0.9448510262521692, + 0.4022170497451182, 0.6565890617824428, 0.3310258105072468 }, + { 0.7856782800988511, 0.6679548733836229, 0.143535331535097, 0.7943042164876586 }, + { 0.04873326306922765, 0.15565317347954144, 0.25192661757985957, 0.4991677708509388, 0.3066370428993622, + 0.16093416033095542, 0.7306599702801884, 0.3010950973671792, 0.8167182072302016, + 0.35620787574024504, 0.08043055169163749, 0.3186418802351839, 0.20232598085979203, + 0.9823046248429211, 0.1332517204586674, 0.6984885530480028, 0.2197570335512251, + 0.3823643227597068, 0.2993821619426701, 0.7890734732591685, 0.9150357080758154, + 0.814073309530792, 0.6466925662323889, 0.21367632233614853, 0.9317957911629086, + 0.902898345030541, 0.6272897679127241, 0.5146060996086259, 0.47521808342344796, + 0.11893945126711991, 0.8681215624426699, 0.8562757163749677 }, + { 0.2813501281398796, 0.423403543513415, 0.06307283742785219, 0.1410750085202912, 0.4171503325036342, + 0.4946182389939746, 0.6531869179899719, 0.10773798427753578 }, + { 0.3042057982656987, 0.36010750174274897, 0.9210922929503627, 0.9192485611716222, 0.5144480274153918, + 0.5814672279115486, 0.6011866421864879, 0.7012821521290435 }, + { 0.9995986452888619, 0.18328112493260273, 0.38935455191203494, 0.3509391406504685, 0.3263088507735412, + 0.9648828416159605, 0.8423623129564872, 0.3871067276945551, 0.45097017756014, + 0.5159198092355896, 0.954050553063797, 0.5014833734710475, 0.7976092097295954, + 0.43190395198337117, 0.12323995739627724, 0.04493903174415104 }, + { 0.07843773812596688, 0.7221954247862206, 0.5131226646525481, 0.9489126584469372, 0.47532447735279626, + 0.5050483053898411, 0.11285163895281858, 0.6442680036899163, 0.6943999719718427, + 0.11854357006570826, 0.9433634243731835, 0.8471613997721141, 0.9645297215939213, + 0.9946433175329309, 0.9445985285947038, 0.07499800505216225 }, + { 0.025606890585376463, 0.12419720934757339, 0.6863742021872193, 0.1591793804631091, + 0.20073179883607617, 0.9163422318494329, 0.8255291764015206, 0.24839917554927815, + 0.9017306407233789, 0.011143738439888695, 0.9499466038149739, 0.23839793578917512, + 0.7530603927443468, 0.7221433441575709, 0.9882231809584526, 0.010882536537176746, + 0.6372138133730175, 0.9850167826732869, 0.6096297429504447, 0.6421948734889896, + 0.4673898464886783, 0.8286049778392416, 0.7412286660738077, 0.7568564633736149, + 0.512257077801464, 0.7639553542893256, 0.019409959095512463, 0.44888036476478865, + 0.5561388696313153, 0.4010303093814831, 0.3519885049606841, 0.36180934217779015 }, + { 0.42957497934079614, 0.3494574872550452, 0.28432737366460115, 0.6512952245877861 }, + { 0.9004930907404661, 0.24177853554090678, 0.2965518122533567, 0.15880453298324915, 0.808380071501632, + 0.534445489902122, 0.6553342025721992, 0.3775806858178651 }, + { 0.05039928002703009, 0.6153252657122776, 0.18969212975794214, 0.6401253643448036, 0.6169843888554394, + 0.8312875187886041, 0.9490105226327502, 0.7245238257008109 }, + { 0.05875630673478127, 0.02409603626667045, 0.8073637232586641, 0.5978959407049632, 0.673078922215489, + 0.5196326131440167, 0.4738950710992147, 0.032500830859891416, 0.8810220711016585, + 0.12909173283303255, 0.9213328273665816, 0.8090874942954726, 0.9354909447748617, + 0.882576819693844, 0.04917162075490544, 0.49253892564640533, 0.3898160399036703, + 0.13157304806755854, 0.6997084267185423, 0.23507481777217032, 0.07693269358017651, + 0.41923240484513036, 0.7701296835014443, 0.9230307316770667, 0.44276343787221584, + 0.4556723674991543, 0.0005269550147345425, 0.9469237088461168, 0.43039381632701756, + 0.9818691560857561, 0.0032385749365969607, 0.38460732810872156 }, + { 0.07177251361521142, 0.597972082172478, 0.6226088866570731, 0.7447140422117096, 0.03608317332120958, + 0.9403392994904969, 0.4214767886988283, 0.5400819129156715 }, + { 0.7886913760270524, 0.2811741704130385, 0.5337400451604049, 0.8427268522792017, 0.43468289560698725, + 0.4474995478449879, 0.4384795264165595, 0.9811467374329375, 0.07392345242810194, + 0.1727386149139265, 0.9717684346647587, 0.9363308310478798, 0.5655367186251736, + 0.7638950609861564, 0.7938449219818952, 0.1853731675532243 }, + { 0.8599654952652722, 0.9067031377676662, 0.9192926722382764, 0.7337945168999715, 0.13609124919880633, + 0.9797115711637998, 0.3065888944026701, 0.1660206808739042, 0.27230041646280134, + 0.3066077606192541, 0.2953550472086268, 0.921215997827601, 0.9959872895290139, + 0.7446027654086131, 0.32904544352394216, 0.3278728444599871 }, + { 0.7347977442629331, 0.2823105544640442, 0.06972698993685245, 0.024892539689898574, 0.8377112725721335, + 0.795264231632406, 0.9831660948336206, 0.6726586022412739, 0.23734748257972293, + 0.27596016754533936, 0.960514274353782, 0.8802020889934468, 0.9420024447650903, + 0.32617198465449837, 0.26334781459438883, 0.9708661080259163 }, + { 0.9849998857013367, 0.9294648448342805, 0.6941639825602122, 0.03962873250027643, 0.34876857441906006, + 0.7102331264346626, 0.009923227971004533, 0.004291243075037587, 0.42771821843373103, + 0.48228770307558944, 0.29210057013301804, 0.10659978052559116, 0.9511736784606385, + 0.6471401175821299, 0.08000089078354955, 0.1422609026335776 }, + { 0.2735420881837255, 0.1525514198463197, 0.46667337710664325, 0.8430422042800253, 0.662646859557516, + 0.4273484753734381, 0.9350218443876921, 0.14772485980464511, 0.35831465507175264, + 0.12425253795744928, 0.700261060470252, 0.7134654616253058, 0.628788146059543, + 0.9719792883450641, 0.06762978640988282, 0.23452033520839022 }, + { 0.38660992002326133, 0.8543958197176149, 0.2379775259300757, 0.9160890057490731, 0.3122712783494066, + 0.7118595870944922, 0.33010723519532603, 0.09124265325192515 }, + { 0.46464059988353745, 0.4182842589733563, 0.6454742040446713, 0.6751443185405566, 0.33610656116635307, + 0.47217147047337293, 0.5013136121720635, 0.8598453024444461 }, + { 0.29252431692420444, 0.5445036774354164, 0.22890028277878705, 0.7981586474384571 }, + { 0.45120803219853756, 0.005439454681410383, 0.29384818661042156, 0.5224946681120767 }, + { 0.6258267454453438, 0.9485563046471146, 0.14234788814134525, 0.6706414835656253 }, + { 0.4429692727609613, 0.9816003222145677, 0.9479665512387505, 0.13103087145529047, 0.9784096110774524, + 0.5867703787286896, 0.7145487664819216, 0.5891205516574893, 0.035029622864427123, + 0.5995473632960368, 0.9423034982217162, 0.53901575498877, 0.8050200304388183, + 0.3174779164324115, 0.7732143797781064, 0.8251941778980239, 0.7277950900374809, + 0.7736491045301819, 0.710706248482189, 0.8498095803008198, 0.8226903167987375, + 0.2884563905752452, 0.9797919189308799, 0.9737107836254583, 0.8415948491227313, + 0.41465219194912595, 0.4183035359012892, 0.2709684010948765, 0.6950743419687081, + 0.9464476910783752, 0.37199873428524965, 0.17782034671421165 }, + { 0.7864927572636758, 0.2418789922428023, 0.017035942650692304, 0.3238822138709484, 0.2964123622659881, + 0.8231199960632809, 0.22959849533167642, 0.5128280021192582, 0.826162071747244, + 0.6149754443882899, 0.05850324347630931, 0.01432072231589221, 0.40266236651645326, + 0.9357647399130745, 0.7675928523784253, 0.3251647144191495 }, + { 0.6712349879452624, 0.8768633991685569, 0.9333296436072808, 0.9719874832801729, 0.9022668072799137, + 0.8633413571017937, 0.816766840903252, 0.6261466465055312, 0.043032952718658035, + 0.5274833759436072, 0.5807849109767159, 0.5534228768710968, 0.6603172096390849, + 0.26813850011980267, 0.23236379162038068, 0.8606794281184019, 0.31203203870718943, + 0.8943901140697472, 0.5400791160983084, 0.4529979440888534, 0.15166416529920868, + 0.5656941220474122, 0.272902982304166, 0.009785280483843528, 0.36102057879912275, + 0.2307220210264832, 0.5668194194053567, 0.2515551306244589, 0.9269562373834143, + 0.4060242417117247, 0.09588656778378668, 0.4471162777527842 }, + { 0.0420959635135838, 0.10953891340341015, 0.02228427097568375, 0.3299764529754966 }, + { 0.9669732816839842, 0.13899760644982895, 0.4414297951394093, 0.8240552945282527, 0.8371972666712111, + 0.10373354060386875, 0.20045983989759142, 0.023505541760897364 }, + { 0.5200251176535067, 0.4039794590721878, 0.7590485264572412, 0.6631651631903216, 0.3989003558798062, + 0.693767248255806, 0.659729341908985, 0.20088033104573177, 0.11011106048831465, + 0.7914612301673087, 0.10174794142966948, 0.06490845280803292, 0.6044074726666192, + 0.30040990308697135, 0.6401444966694853, 0.5159688446015483 }, + { 0.9567414718212607, 0.10348104171557015, 0.25219429120273673, 0.2705602567028519, 0.579440552110363, + 0.11726486785687817, 0.6130270953780019, 0.9521949368720519 }, + { 0.42950635425979655, 0.16518745944643443, 0.13458413628474974, 0.9285861639383158, 0.5740206966788683, + 0.9746846403184233, 0.23986276321155475, 0.7791645261174039, 0.6942989577222445, + 0.10302437381407292, 0.17649726059158877, 0.44259701923012407, 0.12642636197760193, + 0.018563874910113687, 0.6702218490390617, 0.8395756519279107 }, + { 0.9837398631005024, 0.8970651242018116, 0.45841877916367335, 0.9602393019645956 }, + { 0.8525631081163496, 0.9644669729172649, 0.9583040327078675, 0.08720862736937618, 0.38057991297468763, + 0.9172629043363064, 0.21015703777716566, 0.398590750888154 }, + { 0.9553448577457966, 0.3808446826563595, 0.2234692691701734, 0.6931884449795152, 0.4776102415216825, + 0.6379769378552478, 0.18197920456077177, 0.5495763216802875 }, + { 0.2246136780598329, 0.05767747155215186, 0.34443655686657215, 0.23626373941110268, 0.2094919046714232, + 0.33148396737427854, 0.6989592823292484, 0.3075750586735163, 0.30999602181149644, + 0.9444805419325929, 0.5350272394699568, 0.10259125359638843, 0.92251474463751, + 0.5766628546939399, 0.008985724914360338, 0.6161053035242512, 0.6803430663571829, + 0.19806467700559893, 0.8496720223691024, 0.797725223954955, 0.42977026069183044, + 0.2859623992406095, 0.582898925325729, 0.5438471000543933, 0.5567522706171357, + 0.9287397533406584, 0.5315422813533052, 0.7355446639294867, 0.8141949453122773, + 0.5926503762438441, 0.5887798047494944, 0.4067989362143932 }, + { 0.2575640558985337, 0.49455586531763174, 0.23116287667094992, 0.8184204285887713, 0.5791911859886936, + 0.5561610722618936, 0.6207159235834669, 0.47523356819461293, 0.39187688553339817, + 0.11686459005876793, 0.29265769310006484, 0.46897734527879675, 0.7074845790562786, + 0.1194478327696622, 0.7293916292770758, 0.6444884178736836, 0.9230055868902729, + 0.2842944321592319, 0.2891647472388498, 0.9349759803763106, 0.7441865370192212, + 0.19074195964463103, 0.8909968206481071, 0.38663989410484945, 0.7840420007487027, + 0.09858775872012648, 0.6036847723227681, 0.883894562861853, 0.017355209220390688, + 0.17825703513265356, 0.8838845828873518, 0.44563802678121645 }, + { 0.6254574882890985, 0.3396845506691285, 0.5980555987748056, 0.8747864117436839, 0.2157196222997838, + 0.3556636249319295, 0.053806813459211233, 0.2346442931802648, 0.4796148685005919, + 0.24112694504168541, 0.7790663644847666, 0.2409912290072156, 0.22051206627906483, + 0.34180702048144307, 0.4699843195392497, 0.48928572788799174 }, + { 0.7474292432892988, 0.16858497841711506, 0.6628545680181327, 0.6366739239935207 }, + { 0.6545412531900626, 0.9031939852903598, 0.7828131393210792, 0.07194999459094109, 0.3202994846650421, + 0.5967882845856519, 0.07297696421889999, 0.32558559441289847 }, + { 0.9028548797400592, 0.6086914534870387, 0.8503682936674709, 0.08381072759650299, 0.2694175337129405, + 0.7154539330685599, 0.966551261889283, 0.6532594270377007 }, + { 0.7740125823482259, 0.0673750838824213, 0.12989631420367154, 0.0877755681264033, 0.3412272314782776, + 0.6210077405051666, 0.942184839928994, 0.6923106498188738, 0.3584424101818512, + 0.994329394089604, 0.49811988062498436, 0.20711949995802992, 0.8099216616141034, + 0.21757791554483763, 0.27855614423300834, 0.9116369981465458, 0.966439106063462, + 0.804671803881807, 0.7608633527861054, 0.4663260189301722, 0.9812388750291278, + 0.5258364881964462, 0.3721517785904038, 0.9167812394734067, 0.9391534487703991, + 0.6923921715592023, 0.49034089746468823, 0.4070031406140925, 0.7212510294366945, + 0.48610987062902467, 0.9263050142815049, 0.06288898483521521 }, + { 0.8286058511962618, 0.9479079687392229, 0.7472632147955719, 0.826400598301367 }, + { 0.19375704405770544, 0.7452524160661296, 0.6989900431000508, 0.5733421837141005, 0.03610007233720314, + 0.629437685501308, 0.10711005132092344, 0.23736617274617278, 0.47058505183620924, + 0.9474018039618406, 0.41380657435989154, 0.2797356104560911, 0.40189141715709087, + 0.7095520981699313, 0.5270923628575122, 0.2552494536444865, 0.780412379694144, + 0.19845260273689336, 0.18007580659532352, 0.9951276621564948, 0.3505004853158996, + 0.816271984040408, 0.5073348370147777, 0.7662185431411317, 0.29447235072455025, + 0.32016828363095995, 0.5678785309713512, 0.9866334325870364, 0.011830014044379333, + 0.8701512387695588, 0.11728005917738937, 0.837656114582156 }, + { 0.3361744018167824, 0.5488182808161421, 0.2935753360366993, 0.36922806727087, 0.37182055302205785, + 0.5528849186874375, 0.8736774384863598, 0.6420927398375746, 0.431418272383705, + 0.8664139196917229, 0.9245370577416686, 0.7188260085556081, 0.2436988596361528, + 0.20032224181133174, 0.9188259610602927, 0.7837351503328618, 0.14429063324740077, + 0.802472962121198, 0.38298524453499827, 0.9871655455479117, 0.796096414779996, + 0.7821577119408007, 0.7505212770860117, 0.624075838277507, 0.6798108128393442, + 0.9848602164012445, 0.9567333846583475, 0.5780032613282218, 0.32043447337355746, + 0.8090405537644711, 0.8502388833490756, 0.6349005912038598 }, + { 0.9179840062514263, 0.7448040392988351, 0.871309600449137, 0.7649258038573322, 0.716100362833065, + 0.9148935664681578, 0.36841176665067554, 0.06931652410814748, 0.7413457445431846, + 0.4517734106523168, 0.2610255176116475, 0.3345233658972291, 0.40717712479269064, + 0.2818599520541738, 0.9284748949126728, 0.21060462735027263, 0.5435696081303083, + 0.21541396526874246, 0.048166909822641824, 0.034400237572296044, 0.8615299527849822, + 0.4141675345196427, 0.4333206313477377, 0.06532590436538288, 0.2901270916499121, + 0.7036385858160966, 0.8271996390451494, 0.719691017395964, 0.07851349092069604, + 0.05523162021177064, 0.13163226264668004, 0.6533387611647784 } + }; + + double[][][] outputData = { + { { 0.5554009454874607, 0.017189765873370448, -0.12592905534215526, -0.011757491289470089, + 0.07312549803852456, 0.0029102156297631868, -0.19987730488982902, -0.13337758305069133 }, + { 0.7481466528055458, -0.08167572695583827, 0.098339013093207, -0.0814324809790821, + 0.03846232230409691, 0.02581048631100489, -0.006576466032461176, + 0.052609399032017695 } }, + { { 0.48061260851633936, 0.008698421132479317, -0.09098069593780675, 0.15908141170148843 }, + { 0.5545688416281973, 0.07091914301684876, -0.226177140018057, 0.127158950989808 } }, + { { 0.6462796640314765, 0.1207206107818119, -0.01762005486206572, 0.006306293048971567, + 0.06822824850575576, -0.003497250886612027, -0.03677191329183099, 0.09200845566382201, + -0.031055683991221217, 0.006213039230073512, -0.012146295246651465, -0.07738509259143851, + -0.007066788422857184, 0.08229947582855819, -0.08506188580578779, 0.1115861636834207 }, + { 0.43587965950398616, 0.01119474204674489, 0.0436034631759588, 0.054078336398618976, + 0.056574744780298086, -0.04895519013390881, 0.03450482991652083, -0.10638546065835308, + -0.14772029516547686, 0.0429305206422676, 0.0658742677386075, -0.03671568243477094, + -0.08928203226755357, 0.01178332361684557, 0.015146971096621827, + -0.007867575303600903 } }, + { { 0.45732602080629536, -0.1871449382548796, -0.1324116335029496, 0.1291288466731783 }, + { 0.5024038100440398, 0.10371325705289913, 0.15318794362324079, 0.041586591514516325 } }, + { { 0.7061322054525357, 0.16122709645739264 }, { 0.6625256973623044, -0.303254921174071 } }, + { { 0.5582658511001826, -0.014704840584211537, -0.0627157060618794, 0.012456193354444395, + -0.01530010739904733, -0.04788008169393359, -0.03738544216219424, -0.10896799274342381, + 0.128628385932322, 0.07670205169968633, -0.007111511533772388, -0.013410977950846295, + -0.06000928230416999, -0.055453451900711194, -0.045478367624256937, 0.01987054341522309 }, + { 0.6781420149381103, -0.0500509217664482, -0.05026096269054299, -0.014056755596229473, + 0.06615260263105256, 0.0270935493730691, -0.1417803225644949, 0.10813300914922121, + 0.047587732401997485, -0.01667033621629791, 0.10534782624188156, 0.15598626905541169, + 0.026624136604945267, 0.04188891427097305, 0.009064830782525346, + -0.06898328131564806 } }, + { { 0.726816576741237, 0.05886170335761409 }, { 0.4689197740113778, -0.3253844424762808 } }, + { { 0.37769853062274406, -0.06287167118587406, 0.013272760755422566, 0.059798661777511176, + 0.020843863564936352, 0.06255457992886422, -0.020505282720239305, -0.12710747661681882, + -0.056363111364126944, -0.07719436952748313, 0.10480606991456991, -0.014618947965474575, + 0.0014243406910924988, -0.2244391814188424, -0.008451436314684419, -0.00011406707236942537 }, + { 0.5984499821819326, -0.06302615132947265, -0.07883953880314987, -0.004324658112628804, + -0.003873306839043633, -0.11366765114650743, -0.022871220314031784, + -0.13800116659867828, 0.024461602161048757, 0.04561090297164721, -0.017442397228411198, + -0.020345419001976284, 0.016413376549411607, -0.020849024388695465, 0.05947791464931039, + -0.041416211199530026 } }, + { { 0.2272253794003595, -0.04215074100110286, -0.05501389661649361, 0.15128938635711656 }, + { 0.41817336844127917, 0.011572987376696542, 0.11699525680552395, -0.12959128011986537 } }, + { { 0.6261635385326081, -0.12426789261679225, -0.01351449292457746, -0.18417535472553972 }, + { 0.599596012410618, -0.16146991854999235, -0.04177867760967813, 0.11810061116444429 } }, + { { 0.555479274478064, -0.13335701393571267, -0.03840134060123515, 0.20612101698012242, + 0.08392681575466732, 0.09397681364766289, 0.061948998399705385, 0.1699040805655877 }, + { 0.4775145080229961, -0.20516430386037218, 0.07267587187915008, -0.026058009209961, + 0.10395296641445628, 0.005252605556895273, -0.02985365267173483, + 0.05265019142871023 } }, + { { 0.5000201139246306, -0.04800345446956701, -0.0329049463626208, -0.03284470537729227, + -0.20508598415359822, -0.05090945428176026, 0.01485192433096992, -0.06668575548479515 }, + { 0.6977797423695709, -0.08329928338393976, -0.07437114892036442, 0.10800747144622731, + 0.18894316926384197, 0.11583460240335249, 0.017113084069833556, + -0.27560766527667935 } }, + { { 0.4851180273990343, -0.16126949568571958, -0.06691674009092896, -0.10704718961757465, + -0.14611311380319633, -0.035002563984131076, 0.02014526618951898, -0.05833761506438867, + 0.18128233338238373, -0.03264166614264144, 0.12163209118053736, -0.09121827833544369, + -0.05000481625592715, 0.03207004406365072, -0.08147428234704425, 0.015384889697247134 }, + { 0.5677253092727153, -0.03729235153062347, -0.039365455693876845, 0.04146785092046644, + 0.09798188935435953, -0.008627080611540536, -0.06552852244983984, 0.10072436882505036, + -0.08081824922584976, -0.03604185729698669, 0.03292928365596532, -0.0122484248012707, + -0.04163904757760628, -0.02488167514502858, 0.10345023825137331, + 0.039377537425709944 } }, + { { 0.3895162332979207, 0.04005874604287546 }, { 0.4678112991261936, -0.18348392546159248 } }, + { { 0.39940699287949466, 0.11176911860071312, 0.19911545861741672, 0.1902015206428416 }, + { 0.5939351124484545, 0.05900496787177262, 0.137922024588461, 0.0175179665929438 } }, + { { 0.3738855099605134, -0.061514135704676315, -0.2538398050680273, -0.00813228916077971 }, + { 0.7804515639944012, -0.0892065581024592, 0.002545891749693663, -0.07680650878619621 } }, + { { 0.5179707425469033, -0.025302819300953475, -0.08988468070186949, -0.15759688083449927, + 0.04986716326586882, 0.016711033222231685, 0.0893262152057811, 0.011837069383857, + 0.08204319336636628, -0.21049563858248832, 0.015427354145726171, -0.07133856817868453, + -0.01279403797244072, 0.10188626425620104, -0.18206676093811552, -0.07683334214910278 }, + { 0.4557183244222546, -0.10900814441739004, 0.04904230378999857, -0.0010906845210327046, + -0.031895227628587916, -0.037331861262286915, -0.040831099380204676, + 0.0030723082051012374, -0.10402962094045479, 0.09107053711775343, 0.14231804953997862, + 0.06072556870526918, 0.015183021067558186, -0.023570386570056008, -0.0692160119825995, + -0.010341036241630921 } }, + { { 0.509266881164118, -0.23777343990417177, -0.16207618102797572, -0.037644746616759095 }, + { 0.4844952936065516, -0.13303389385421258, -0.25571531259653263, -0.05966291383459679 } }, + { { 0.5935176438976462, 0.005419535054208113, 0.05452812784476206, 0.05483345393049244, + -0.04461918309489521, 0.03860170688009229, 0.008260547169506763, 0.07814954434523974 }, + { 0.5579264002751396, -0.19510727214536616, -0.2776691321309364, 0.01833209516889496, + 0.043341981649842765, 0.09601908528205956, -0.003869164267408187, + -0.16505054140412423 } }, + { { 0.6260210272262958, 0.02432692813809728, -0.003993313239643373, 0.12917970105240434, + -0.07053644945003956, 0.16506423245734803, -0.05346289230457359, 0.04336626138538329 }, + { 0.5241234456299799, -0.061299666157965914, 0.20381084080210454, -0.03215905928455756, + -0.05095139644888391, 0.03355389235031433, -0.04283903698729301, + -0.3019386034408972 } }, + { { 0.5500660037041453, -0.0756423721419075, 0.2210689971105149, 0.07481755482487502, + 0.10628452169723956, -0.1243776248752409, -0.09116501409436648, 0.07374567803767317 }, + { 0.6070515456890231, -0.3012803549751432, 0.04193893532024, -0.08007158532442629, + -0.006248541615777148, -0.10324316179539066, -0.05306697572107942, + 0.1322676210022765 } }, + { { 0.4651842021869838, 0.08699362121518653, -0.03136073598867131, 0.10751032697033988, + 0.04427971547591952, 0.019039286768015494, 0.18878104838596632, 0.1045724206875964 }, + { 0.39116023270347816, 0.031180108596409038, 0.32532105395892885, -0.11704207031957495, + 0.04658810674925612, 0.009016215044166007, -0.07362344496447837, + -0.1848819833344538 } }, + { { 0.4885688910675007, -0.18086781613949704, -0.07671903790273893, 0.14878362006771667, + 0.09590215124139359, -0.07456619521904288, -0.03965753053553456, -0.08790199439607198 }, + { 0.474901408893455, -0.17244598635312056, -0.023956901826718974, 0.1791658005916335, + -0.036152996890597366, -0.012259503214089251, 0.07875989038950917, + -0.12969705651831892 } }, + { { 0.5987680678550062, -0.11799613493734534, -0.2864743448783378, 0.19231233198393816 }, + { 0.36137018847278746, -0.019882285719344417, -0.04018093170042117, 0.010964307296384701 } }, + { { 0.5508858453605304, 0.051710056952484845, 0.004171556603573934, -0.14212685903305175 }, + { 0.5423592365640588, -0.10551677764322767, -0.1236491498948506, 0.02291325214037246 } }, + { { 0.4185139971798104, -0.12598968025560597 }, { 0.5135294651086221, -0.28462918232983503 } }, + { { 0.22832374343997397, 0.2228842887585636 }, { 0.4081714273612491, -0.11432324075082756 } }, + { { 0.7871915250462291, -0.1613647796008854 }, { 0.40649468585348525, -0.26414679771214 } }, + { { 0.6380761918458395, -0.022978944297255756, -0.0014396124781275166, -0.02384143615782447, + -0.07921880781102483, -0.06801029020723776, -0.15490186756072524, 0.03355342032660816, + 0.06685652476192966, -0.01586151971323415, -0.13389162215415135, 0.005533156811150963, + -0.060356774511329564, 0.17693167298435386, -0.03612458427971646, 0.11864376520170625 }, + { 0.6414668453372225, -0.012718518336902778, 0.024705495293850604, -0.07647416157394636, + 0.06296179906251137, 0.11361886055723146, -0.02420694444897769, -0.03841719639006607, + 0.05452753410368566, -0.01239440538295344, 0.032061027655300084, 0.06376752807408094, + 0.012832470978494881, -0.07360848144902933, -0.01965325840198135, + -0.020673505041039646 } }, + { { 0.4039060952260403, 0.17105094720582825, -0.04687300825237843, 0.06312719676982903, + -0.07152120584803218, 0.12874155376912266, 0.2559406786392022, -0.1178795002459361 }, + { 0.4931432693943547, -0.04258744279908336, 0.07887713934060492, 0.012311293243742596, + 0.020586864135253208, 0.09532173093653501, 0.021804946261635733, 0.1467042712342011 } }, + { { 0.6492600132374694, 0.07405704598578203, -0.10652150413678427, 0.014145446840794215, + -0.0763605684643836, 0.07600883542245865, -0.03496573571710465, -0.06855537550667139, + -0.04424787015115095, 0.06760348676948996, -0.09178162857206457, -0.025320785111737663, + 0.040561414773794824, 0.13320415864997182, 0.021189849362183932, 0.042958204563214517 }, + { 0.40535288984911627, 0.17032068199517506, 0.01331219400387537, 0.03853473160782414, + -0.012526554240257781, -0.06187945408577552, -0.036479509907725347, + -0.07188532577134454, -0.0019327516265471578, -0.08139500313648848, + -0.0058851768933687165, 0.025922987062677243, 0.047024671064922474, 0.0211940396143128, + -0.072339453496859, -0.06530692733234739 } }, + { { 0.07581743845849698, -0.03372147494491318 }, { 0.17613036197559018, -0.15384609099990643 } }, + { { 0.5928639944503687, 0.11132887192540089, 0.11133754396132797, 0.15144287134688658 }, + { 0.2912240472333921, -0.012080065326201012, 0.2276045060510091, 0.3304487787130108 } }, + { { 0.5374369429329483, 0.0124309756250367, -0.1263557788347015, 0.010780749436562376, + 0.0469888925419365, 0.15244935362834236, 0.0013926801264731864, -0.1150986978030912 }, + { 0.39114492523974376, 0.049146273279162184, 0.02579091351846495, -0.14222691912065577, + -0.027042182426221584, -0.14789058018667428, -0.03263438975452017, + -0.0061769800609844105 } }, + { { 0.39574426536060486, 0.3848693124084244, 0.20872361615139384, -0.03259572209916245 }, + { 0.5654818630543237, -0.05016643956373015, 0.030751960689858693, 0.03337316792991071 } }, + { { 0.5281995925319434, -0.013483007703420306, 0.22370807173452606, 0.032600345010997386, + -0.183706104923201, 0.10078556921052059, -0.06643803387393597, -0.19216007772763355 }, + { 0.3839006686515898, 0.04203486886439816, -0.07423404752770826, -0.0167526930749399, + 0.03296043868103443, 0.07361362334006122, 0.06773560004500725, 0.1850404987428018 } }, + { { 0.940402493651157, 0.04333736944934541 }, { 0.7093290405641345, -0.25091026140046113 } }, + { { 0.7156356852777146, -0.15610326950991757, 0.189797885134394, 0.10323280721415865 }, + { 0.4766476514940784, 0.2619203051863527, -0.18127917611815178, -0.1767088675875917 } }, + { { 0.5632118136379611, 0.16086874310016572, 0.026195249820023803, 0.20506905118764587 }, + { 0.46178567640449736, -0.004178181340561221, -0.1319909533632702, 0.1519936998210166 } }, + { { 0.4016790839699139, -0.03003822458401474, -0.057149866644219526, -0.011524063301240554, + 0.034856641495165167, 0.13554174060998003, -0.09095916185362438, -0.0018328637444979812, + 0.005074060125136143, -0.03801226191198792, -0.043716698741379005, 0.0030306419969509946, + -0.024955698295149528, 0.007269331103218465, 0.042476489879821895, -0.10712547204424006 }, + { 0.5952054191724998, -0.07676059511106324, -0.026180524219684337, -0.04836907409057477, + 0.03599606184553125, -0.09752988130462281, -0.018060463584829087, 0.050791615691301736, + 0.034038777924507435, 0.07585998009242355, 0.04676163922757573, 0.16844664234113793, + -0.044975123197931816, -0.04892751473324741, -0.004238118680509565, + 0.03828422498466859 } }, + { { 0.46901212184076757, -0.01535709462655225, -0.03916243436874867, 0.060857712029923704, + 0.1225909913945583, -0.09340926175888574, -0.1289223230381446, -0.010663880940625405, + 0.00724348179779008, -0.060944417834272645, 0.06976206691384879, 0.03620812577362522, + -0.11481741841389, -0.045575265138713114, -0.060986015410215574, 0.061727667678068 }, + { 0.5337093691722835, 0.04436500074224131, -0.030963373409186423, 0.020253982238142385, + -0.0824519992966499, 0.012010153722473894, 0.15829867840518805, 0.04533676610748462, + 0.1083306629496745, -0.08650086214010269, 0.045865730697915885, 0.026540593157353194, + 0.05755930064433876, 0.03279325594388378, 0.06317542465592338, + -0.025317096700691388 } }, + { { 0.4122273004184882, -0.014566204465011313, 0.04208221121586804, 0.17869124295327457, + -0.038967419712763435, 0.03973015972596076, 0.005246463372848328, 0.0010137347804333452 }, + { 0.4077985676527511, 0.1686350835680268, -0.12037578482090629, 0.006478097285106572, + 0.07949583704816715, 0.032202813316253534, -0.016855152490183614, + -0.0777645930586234 } }, + { { 0.45800711085320694, 0.2894221324360919 }, { 0.6497642460058267, 0.013090322012305977 } }, + { { 0.6031245930981106, -0.09986864407594251, 0.11555260315746024, 0.035732701010434215 }, + { 0.3289125819706231, 0.2696416277863902, -0.13227435752865208, -0.14598036756331914 } }, + { { 0.6114313386227679, -0.002426979989567729, 0.2651802480809971, 0.028670273025861864 }, + { 0.651170538927121, -0.04306325057145169, -0.03318614112600926, -0.3055036135167195 } }, + { { 0.4957183696678124, -0.10553486119506748, 0.052758247615336755, 0.06020035276185396, + -0.013822765236362097, -0.02389059354934929, -0.08857441873167826, 0.008621444261494117, + 0.02082676340882711, 0.05884859157016205, -0.04918841735798534, 0.1405177719428041, + 0.0681786035653371, 0.10947244481774009, 0.08033111333375087, -0.04045006452645019 }, + { 0.6574845762838595, -0.031453441409394496, -0.04620430117138156, -0.08424489796710648, + 0.06624180213432235, -0.13120364199595552, -0.024391758054135494, 0.08573068590472949, + 0.11223336151893865, 0.07527943774180709, -0.0024984661992818805, 0.09946105848481208, + 0.06606087488780027, -0.022974547547147776, 0.12387018801680866, + 0.02304817543478708 } }, + { { 0.8882569099677424, -0.059651058771480525 }, { 0.7868319065484695, -0.03956869175289757 } }, + { { 0.45166687758041546, -0.0018649641234558647, 0.04104745825547753, -0.04331775004206736, + 0.04599538731492686, -0.03347890915900812, 0.05706572867550946, 0.04407075696339463, + -0.09550030045209212, -0.01294296290526355, -0.028171062627338407, -0.011479435442369852, + -0.126578568096198, -0.1055877835747785, -0.01335447270374341, 0.0261870443942967 }, + { 0.5375290203239034, -0.02717126518144706, 0.03769657408945279, 0.05903704597317041, + 0.10941228619740563, 0.034836047977018406, 0.11449317534415826, 0.03329000205035401, + -0.18630596238167652, -0.03847833560601357, 0.11119698020303745, 0.10426826809732498, + -0.10133153669488919, 0.06085072384786873, -0.08524817187204464, + 0.016337527326520968 } }, + { { 0.5672530754492042, -0.07621450260422805, 0.04780501932723003, 0.1360648011834046, + -0.13636811964603351, -0.05559164579848517, -0.07319574079493588, -0.022375181723106995, + -0.01803709042623941, 0.012598739926626794, 0.003834329770772228, 0.02279304289413908, + -0.06706984366225677, -0.023519044517253875, 0.05957470708250281, -0.041378144644557704 }, + { 0.6927367377783716, -0.08658190014174541, -0.044710161680140095, -0.10627851813733222, + -0.08408056627365093, 0.02806037261656114, 0.03681651148127257, -0.0438823647392234, + -0.08259784729478026, -0.0660019535766834, -0.06364239429924656, 0.005461792317948917, + -0.04090024064986589, 0.022673859550358066, -0.0015713160185880162, + -0.021211377685855435 } }, + { { 0.5615331442331852, 0.09973756929788653, -0.043561441091611794, 0.10577901659492642, + 0.027355604592400162, -0.07846639287809637, 0.06769255556633608, 0.11495862363660969, + 0.08994548302237715, -0.029898108867756554, 0.07176626712854703, -0.04707555302652763, + 0.016817577757129054, -0.14296761759104473, 0.03811568418894253, 0.0662515936881235 }, + { 0.37970420079142386, 0.035427020131707, 0.03758585103249978, 0.09252346890432356, + 0.08421133424276199, 0.0013340546210409143, 0.021541970955435447, 0.03079966148676702, + 0.022053247502089618, -0.05621550573850214, -0.07144813696100247, 0.0005035402070686645, + -0.04253374666480074, 0.16004586961594686, -0.01426637100829721, -0.1376968509881538 } } + }; + + for (int i = 0; i < inputData.length; i++) { + double[] re = inputData[i]; // Real part of input + double[] im = new double[re.length]; // Imaginary part of input + + double[][] expected = outputData[i]; // Expected output + + ifft(re, im, 1, re.length); // Perform IFFT + + double[][] actual = new double[][] { re, im }; + // Validate the IFFT results + validateComplexIFftResults(expected, actual, i + 1); + } + } + + private void validateComplexIFftResults(double[][] expected, double[][] actual, int lineNumber) { + + int length = expected[0].length; + + for (int i = 0; i < length; i++) { + double realActual = actual[0][i]; + double imagActual = actual[1][i]; + assertEquals("Mismatch in real part at index " + i + " in line " + lineNumber, expected[0][i], realActual, + 1e-9); + assertEquals("Mismatch in imaginary part at index " + i + " in line " + lineNumber, expected[1][i], + imagActual, 1e-9); + } + + if (lineNumber % progressInterval == 0) { + System.out.println("ifft(complex input): Finished processing line " + lineNumber); + } + + } + + // Helper method for asserting equality with a tolerance + private static void assertEquals(String message, double expected, double actual, double tolerance) { + assertTrue(message + " - Expected: " + expected + ", Actual: " + actual, + Math.abs(expected - actual) <= tolerance); + } + + private void assertComplexEquals(String message, double expectedReal, double expectedImag, double actualReal, + double actualImag) { + final double EPSILON = 1e-9; + assertTrue(message + " - Mismatch in real part. Expected: " + expectedReal + ", Actual: " + actualReal, + Math.abs(expectedReal - actualReal) <= EPSILON); + assertTrue(message + " - Mismatch in imaginary part. Expected: " + expectedImag + ", Actual: " + actualImag, + Math.abs(expectedImag - actualImag) <= EPSILON); + } + + double[][] reInputs = { { 0.6749989259154331, 0.6278845555308362, 0.995916990652601, 0.511472971081564 }, + { 0.5131170807843421, 0.21947931088584838, 0.8291802449178103, 0.7771605380836767, 0.8702643857540487, + 0.8710395079045988, 0.38542176916377324, 0.2881994247798211, 0.1928317731008281, + 0.26952861717454657, 0.15144147822174414, 0.41960334787295883, 0.45034112558576034, + 0.1583971541867757, 0.957780647411057, 0.6758004971959929 }, + { 0.307168369495708, 0.2925436063741176, 0.41391507728373833, 0.3005219437466511, 0.5878469029586312, + 0.12931493281234274, 0.9278465629379351, 0.17121813246774298, 0.4177834301993081, + 0.3769141990814494, 0.6753379435396163, 0.38247139181867484, 0.24735225836867192, + 0.31494194715109947, 0.8137890716670311, 0.20924977998686056 }, + { 0.12235304422259108, 0.8698130261511119, 0.4337042152439805, 0.6857925372785122, 0.14428854345470843, + 0.33600839167062124, 0.8119488842140201, 0.11798945578653453, 0.010947620425100557, + 0.9717416040360395, 0.12125654222557414, 0.2617819654240723, 0.6486301555911228, 0.7365333531211434, + 0.2853381645844496, 0.245649529407872, 0.6297339098238705, 0.27490823498920813, 0.16976743283098572, + 0.6735736955293987, 0.6966873975788616, 0.03317205897839892, 0.06774514015627431, + 0.2345911011238595, 0.056861430818630154, 0.9458602964130863, 0.809611874188032, 0.2508944819690482, + 0.41904987235844393, 0.44527473435246423, 0.3598767804057533, 0.6738051864030766, + 0.04313688787812686, 0.14040411518698392, 0.6279634929162148, 0.6907852049110121, + 0.010900703530978717, 0.22548934759676642, 0.42249315955719224, 0.7566767789254828, + 0.5549511666098619, 0.2132782383128563, 0.13319453700750528, 0.9913307843402898, 0.5918930102432697, + 0.8123119094264839, 0.06669536255452357, 0.1404352024672152, 0.5822461498734745, 0.9507715524385827, + 0.5149268016834162, 0.46487304063142065, 0.14177878291874513, 0.3030540409436946, + 0.7390807099554567, 0.07855298022742563, 0.7267851554463288, 0.3003808861768813, 0.6942310295833771, + 0.02659985118413699, 0.08047625367936184, 0.25129044839422787, 0.21921500696568008, + 0.6446997331877551 }, + { 0.6467394714302301, 0.37939437912607255, 0.24389382952413974, 0.9230517610649195 }, + { 0.8133403772356219, 0.9685127164754849, 0.9527118851443901, 0.16168777807158385, 0.3059554069336482, + 0.5098875351782225, 0.18776670485231783, 0.9552937947531795, 0.26630009198521765, + 0.7607275302116739, 0.22743564839228736, 0.2579812237254093, 0.16461081160421187, + 0.2568798739085394, 0.6664580672719578, 0.611154187704012 }, + { 0.27346385127769657, 0.9768706836769414, 0.63435141212605, 0.09267578126266052, 0.6091291796709725, + 0.8003601253351955, 0.21441231066064415, 0.7574882595853318, 0.6175964091894415, + 0.026447112091714353, 0.9838155477830359, 0.2879060542830987, 0.293010252419861, + 0.44472456980740893, 0.18371859899106335, 0.9697152014778653 }, + { 0.9241946993360354, 0.93609012256163, 0.7262744168234273, 0.8953331805633857, 0.6737803589116659, + 0.12819148236368838, 0.18661306972239466, 0.7389050320153769, 0.7671764150606258, + 0.4513549998697196, 0.8730680721562523, 0.5506214841129061, 0.03430880982544926, 0.7895272245140907, + 0.9546984540561432, 0.42833058697767035, 0.2900777210978328, 0.8562805913655864, 0.5473173303230449, + 0.4148313135430701, 0.24482377481230033, 0.1537057907732975, 0.11035564325990421, + 0.5386796809145485, 0.8661412254170333, 0.8498517303709959, 0.5906863904920534, 0.9078942281308974, + 0.7387794781959018, 0.720441512828483, 0.9561370849174334, 0.3347594847372559, 0.48786193718206394, + 0.9919064656347301, 0.4058367062974416, 0.8130018855584936, 0.7671992644775938, 0.7621070450721142, + 0.2968734799496455, 0.08903123300124016, 0.8438017683491356, 0.9911353232857506, 0.7124852788354706, + 0.8772702766537811, 0.34692831949403335, 0.6506408096815364, 0.22319555392893964, + 0.33156033109536054, 0.8234621132297556, 0.9015756069246127, 0.4512357762048391, + 0.12584571471970663, 0.53112509650375, 0.3972399589722395, 0.5149797348786508, 0.7327411500784157, + 0.45127893858601775, 0.016770807338283178, 0.26719900538319863, 0.3129575148836051, + 0.8896414390923291, 0.40604886799160245, 0.11009325815063131, 0.2697706263683922 }, + { 0.8287813507102393, 0.1908501784393708, 0.2003356406350958, 0.6681197614203025 }, + { 0.06238193735772257, 0.27323433977164435, 0.040380413052192865, 0.19662508742378038 } }; + + double[][] imInputs = { { 0.8330832079105173, 0.09857986129294982, 0.6808883894146879, 0.28353782431047303 }, + { 0.11858471405750115, 0.6670449264234233, 0.7918053251189741, 0.18474324421305366, 0.2267737011892611, + 0.6112631028641866, 0.43732697326158654, 0.024562867771134922, 0.5846651001654705, + 0.9231757238526722, 0.32785888537720953, 0.058059707791723425, 0.888849882806027, + 0.34043171288025054, 0.37505905249694726, 0.4989225900943882 }, + { 0.19515621126922889, 0.9663577672470134, 0.052248997892968774, 0.3368420822314041, 0.25591814564245663, + 0.31140858941730654, 0.6214194316782065, 0.898776506616156, 0.21908542333722314, 0.1721154555352078, + 0.8129941790492901, 0.11019299590688614, 0.9358894676906747, 0.5566948949725117, 0.5712632308813113, + 0.6198873277092072 }, + { 0.7553443433772105, 0.7149725143780661, 0.26342945108194404, 0.6073384956030843, 0.6682926680358615, + 0.9391571286734728, 0.6857282535520356, 0.8074713507427728, 0.9936095082352023, 0.7050591882477736, + 0.8307563345962672, 0.4504495350884602, 0.6669656135676392, 0.8229995389638302, 0.9724224050712397, + 0.4465681389737993, 0.830115912596753, 0.5758021742895939, 0.2555926327785295, 0.7229147142946879, + 0.19465587302731124, 0.6455358466934192, 0.9895816229678752, 0.27617594094673303, + 0.27676552480841954, 0.950721135261892, 0.06159792234410011, 0.25388932381509377, + 0.027723112627898172, 0.6255532725359773, 0.8145203264634711, 0.8254031856352116, + 0.25282878966017297, 0.5652351235601151, 0.43847171025625065, 0.2970657957526097, + 0.03860912616964929, 0.9667833637002887, 0.8313877744010215, 0.8064932753796599, + 0.04382793626063919, 0.23236021244230354, 0.9946493719154478, 0.9442462780462477, + 0.34190146836022484, 0.4801148300313609, 0.4347269847824623, 0.1934175418869568, 0.9493835173004894, + 0.008847061440726112, 0.43992481101923, 0.9975703488985399, 0.20833318425512648, + 0.39400365256608416, 0.43378030144507285, 0.2626310071775566, 0.35102954497285954, + 0.9000857989597337, 0.960793699725036, 0.4818308285625894, 0.258367005138464, 0.7955496343862104, + 0.38227049297061555, 0.5309262232624709 }, + { 0.030027811041104635, 0.24756223021053136, 0.6932959203102655, 0.31779244778670823 }, + { 0.057812427817348744, 0.48563678221308515, 0.6385926917596327, 0.06866424557822048, 0.7286057309387103, + 0.578198895275423, 0.35806649041869165, 0.17544934833578907, 0.9546365831179597, 0.7608272984745191, + 0.18047691393328225, 0.6719741912319742, 0.3254545042940935, 0.5391892808596687, 0.4610969186702265, + 0.6058329787299822 }, + { 0.5731193504743051, 0.8285838565973793, 0.033091935005607365, 0.9929008886851187, 0.2702292788833681, + 0.17372141606262959, 0.24616279825199905, 0.759464525093345, 0.37795458115492475, + 0.7612050359964757, 0.3364320358467179, 0.8518930134503825, 0.639680370851991, 0.5647886652079595, + 0.06383168132690153, 0.941871175081797 }, + { 0.10654574064225608, 0.18112904630201787, 0.7817037292161019, 0.748279488764987, 0.7485017168905912, + 0.4270901026653211, 0.5037981228936685, 0.8413518361670483, 0.8918350666826977, 0.8960201986678724, + 0.04380182167274471, 0.36899360872947595, 0.4792421777277245, 0.09414311412615073, + 0.9810071872876617, 0.9701739706435585, 0.566192946273844, 0.4761038133937181, 0.07930270657906191, + 0.6958808238326885, 0.07288965713564122, 0.32843359695282626, 0.27790842403879157, + 0.6387041119338002, 0.15788528537840396, 0.2667380867035477, 0.7715682128215527, + 0.09996918315097514, 0.2690373433561819, 0.1530087585637756, 0.3879254571666868, 0.3251278338384528, + 0.2663572874336204, 0.5826546948845359, 0.8831125706700407, 0.5502460958047991, 0.4710428569771352, + 0.7530740137353021, 0.9754266619245848, 0.377421578189048, 0.01652242589646613, 0.8244149593142929, + 0.6820110251541992, 0.4002296116241395, 0.2955913658044953, 0.7362095700182021, 0.40770234279226714, + 0.87172045299516, 0.039230936916690995, 0.02750586292612267, 0.10078915278495215, + 0.8983285228828739, 0.32618356004699833, 0.6886593271816672, 0.07577625227317097, + 0.3447942876325266, 0.39914263324350274, 0.827412575862219, 0.34354508675330786, + 0.09570201879504958, 0.3370302990354914, 0.8158265013181055, 0.6293777092101274, + 0.431976260298389 }, + { 0.09433683850154906, 0.6852026530097579, 0.32427852025540604, 0.25548685811214333 }, + { 0.04917788114994759, 0.3254526123103906, 0.007583733861966979, 0.7225054664397857 } }; + + double[][][][] expectedOutputs = { { + { + { 2.810273443180434, 0.5315583899556339 }, + { -0.20450648028789575, -0.43732964918644013 }, + }, + { + { 1.896089282928628, 1.1318539117217823 }, + { -0.03276314452169382, 0.3371527815133526 }, + }, + }, + { + { + { 8.029586903023583, 1.4783572816608272, 0.6711701068551449, -2.0728968306396376 }, + { 0.5021953651101563, -2.5648553213785314, 0.29423626194475117, 0.4012601985906169 }, + { -1.2849021209400728, 0.6671323763730415, -0.6695725808397565, -1.1712843044117673 }, + { 2.108868551493044, 1.0843197356522687, 1.0867961189703699, -0.35053844891456276 }, + }, + { + { 7.05912751036381, 0.5291423797442222, 0.4427197585821442, -0.755496055817138 }, + { -0.30418687059677985, -2.2598935800730793, 0.6047902140223604, -1.5847243484575326 }, + { 0.25274774363624575, 0.05354074350183502, -0.46293891370557894, -1.4928446525217676 }, + { 0.04102445584853265, 1.2150529213724437, -0.3501635847389326, -1.0905422962407672 }, + }, + }, + { + { + { 6.568215549889579, -1.2298598996976158, 2.2138636830117013, -1.3116154891143874 }, + { -1.1345702156384125, 0.1358688749140914, -0.869135880783189, -1.2945083094997623 }, + { -0.23490362681105115, 1.8841337467288004, -1.290356324016746, -0.7998632424301513 }, + { 0.057854280160745564, 1.3009331869650396, 0.4577001084229444, 0.4609374698297427 }, + }, + { + { 7.6362507070770524, -0.5021300289612729, -0.30830053219433307, -0.40162315416311345 }, + { 0.00532353080901915, 1.0369754448667339, -2.483776588979377, -0.016217813328190278 }, + { -1.9062644821386074, -0.3728019954908228, -0.3037464465492685, -0.527450990728018 }, + { 0.46711047881499734, 0.4414987825805363, -1.1273549935419012, 1.4850074622342282 }, + }, + }, + { + { + { 26.68609298551157, -1.3338953635792201, 0.4020594424454147, 0.7401299103272476, + -2.810554548459751, -1.2778878008097723, -2.434717541683333, 1.8448958370317132 }, + { 1.5124772568141867, 2.997435519224954, 0.850391793491705, -1.409268535773998, + 0.7298732550934647, -1.0941883198448044, -0.4717054661204718, 2.2295601902248148 }, + { 0.9413326138415741, -3.5206438979732253, -1.860288065902871, -2.154762617920012, + 1.4998965860283968, 4.037874687522323, -2.97033202289703, -1.0678593057556733 }, + { 1.6739716993045417, -0.05711258741013203, -2.3676553799709974, 0.5456868972025865, + 0.1948281831859553, 0.7526107332947336, 5.065447419515738, 0.020456210203669833 }, + { -0.6956713490957522, -1.8296524399484828, -3.1697164039855377, 2.237563147861894, + 1.4551539353995173, -0.19554352732365277, -0.4636431658812785, 2.8849067509584243 }, + { 1.2874212252086117, -2.007455023954, 0.41828409822144785, 2.1560540097992273, + -0.9444730547029133, -1.9570758407351865, -2.7449964653128944, + -2.1931578153825004 }, + { -1.1727630961580466, -0.4626944940820328, -0.6026604126017501, -1.147197715423295, + -4.969175678925014, -1.8568706158045902, -4.704146412253633, 3.2664484067588457 }, + { -2.0576765512500503, 0.06751753454362419, 0.412051207702436, 0.026723509175442417, + 0.8659815323685047, 0.6298326096441218, -1.0225568132581624, -1.5743660274808011 }, + }, + { + { 35.87455968396184, -1.5248270601889629, -3.804141676368115, 1.8347304094358663, + -2.5797852364328016, 4.556415677676634, -2.059620257585239, 3.3259090771947486 }, + { 3.8330193771856442, 3.96923392685639, -2.4257908732841234, -0.5143874873871535, + 2.544553958429333, 5.542586595696317, 4.951043571220989, -1.6001504185486546 }, + { 1.572704438375391, 2.68337930664312, -0.8033985360422281, -2.6558624309276784, + -0.40085422767947554, -4.000393284174368, -4.331102876667192, 3.506899629433532 }, + { -2.4680885926531975, -0.8695810917508298, 2.9456617602301707, -0.24732914730729616, + 0.3430542822308015, 3.485849838657556, 3.9649335464206823, -1.680341105583727 }, + { -0.22764415191794996, -0.4951316522431507, 4.061903443125903, -0.6424984523403212, + -0.12529040791295087, 5.354293685375573, 0.040525918509912495, 1.0133620066630307 }, + { 0.6469092912664327, -1.9089096532567051, -3.0326019092676626, -3.6447657932795416, + -2.122099108477178, -0.7865053946835596, -0.1566125392915363, 1.984921157087272 }, + { 1.3348166868775824, -1.2189282274599857, 1.2479841062752444, -0.948271682640534, + -3.975769852998465, -1.562443050085742, 1.6163117823648547, -4.4062925310517995 }, + { 2.9675969104598394, -3.280556379469013, 2.3899027415100598, -1.6067363579675273, + 0.7470324060379839, -2.499741728611057, 2.986670707313758, 1.6217252291607327 }, + }, + }, + { + { + { 2.193079441145362, -0.41181283923662215 }, + { -0.14081174003275643, 0.9465030238449373 }, + }, + { + { 1.2886784093486097, 0.15796905335413058 }, + { -0.7334983268453378, -0.593037891692984 }, + }, + }, + { + { + { 8.066703633447759, 0.35776587504447654, -0.8975456466084525, -1.3260971108489834 }, + { 1.2925550450271355, 0.7471970785662574, 1.8523195957640421, -0.09130567177735105 }, + { 0.75069086903558, 0.45287128407866684, 1.1193031551551829, 0.11343169446248846 }, + { 1.4750614801978492, -0.4474301227850148, 0.46932996702100105, -0.9214050900106854 }, + }, + { + { 7.590515281648607, -0.08161444013345664, -0.18103075974871663, 0.9381669029060151 }, + { -1.5770093406180952, -2.7484477676036216, 1.3742861198499816, -1.2015040141484536 }, + { 0.046726986603437215, -2.150769239162087, -0.1301370419904344, 2.067734397359103 }, + { -1.057408338160801, -0.5695893624855741, -0.4947019509681271, -0.9002185882701961 }, + }, + }, + { + { + { 8.16568534963898, -1.440928805449021, -0.5466902254014514, 0.9947324514433773 }, + { -0.6989972691812494, -1.187942183202632, -1.062436068043263, 0.09504092090567329 }, + { -0.3794316462577032, -0.523294612518167, 2.7973454035250693, -1.9389358312439806 }, + { 0.8221904791733652, 1.051347229425449, -1.835143916223776, 0.0628803438524741 }, + }, + { + { 8.414930607970902, 1.0408479366310592, -3.3339265443792727, 1.3220823252356668 }, + { -0.390009888242036, -0.7239135908698271, -0.5199660526428092, 1.1500729000277474 }, + { 1.0954307864509207, -1.1232199355230432, -0.8940432401163299, 1.086489516763935 }, + { 0.590432616869855, -0.5703843580205217, -0.11315800207193116, 2.138244529504565 }, + }, + }, + { + { + { 35.97203267785346, 0.45863783607284137, 1.173056641524758, 0.7826325498969262, + -0.7567714479514733, 0.8900030139481167, 2.3340075668593485, 2.7783597078640225 }, + { -1.8297969735165558, 2.273178623633307, -0.48372099384482214, -0.40852587671049156, + 3.760728469728649, -0.7610308335136011, 1.2919699452742253, 4.670040243629213 }, + { 4.8372690355107135, -1.3261208568775944, 1.46633732066654, -0.0992228827598397, + -1.9223527786243282, 0.44458038637838815, 1.5533185398931715, 0.24496224499509434 }, + { 1.6661974146730905, 3.8314516505325447, -1.589204977232685, -2.9696832529162926, + 1.079869594848176, 2.8153541217824465, 3.0882863865289134, -2.28027259208448 }, + { -1.0570779237084942, 2.806843738194792, -1.4276915996739796, -3.4905691035061417, + -2.230138814152109, -2.872817891174476, 3.932782623656472, 1.1162539578269415 }, + { 4.289222018755856, 2.105500347856227, -1.0762009261508743, -1.6228254057953164, + -2.190533559398732, 1.1004383893378056, 1.1709877234229678, 3.1232851514701316 }, + { -0.45942227177196493, -1.819905457869288, 1.4446474205856596, 0.5520635899236316, + 1.3655329844463444, 2.67212745690105, -0.57694981659637, -3.6005960797530303 }, + { -1.743365079415264, -2.335615239269563, -1.8778197199771323, 1.4330640847642324, + -0.6075926305809934, -1.8295061379841517, 0.5375950122938771, -0.668826591213622 }, + }, + { + { 30.10531167057931, -3.4682674523063, -4.122859779991433, -2.4814276275979648, + -3.3693361452179884, -0.08565925201122448, -0.8401905476029238, + 3.8121277138883807 }, + { -0.028543509160374403, 1.4086455375421374, 7.027677724316744, -0.8029732853849363, + 0.21508257722145652, -1.2788348742349065, 0.7592532617840794, 0.09119198095300907 }, + { 2.4233994453629304, -3.012051635580196, 1.1558640736544619, -4.573157481233569, + 4.20161895289186, -1.204816459987149, -1.6145773913793897, -1.5849104998999013 }, + { -5.4157739368994235, -3.7882855945306204, 2.7772761792419294, -2.4584113040850095, + -2.069213530724667, -0.5478843826867934, -1.6160901693215453, -0.6568154923833014 }, + { -0.43647261868644627, -5.5334868871320335, 0.40365819731692065, 2.150018598987577, + -1.2004536158862775, 2.4388808649611087, 0.23590045802305715, -1.9545129970611796 }, + { 1.6309381678509949, -1.9880205240060747, -1.7739681748314111, -0.29204772903330056, + 0.8738754999325199, 0.2691425045145516, -1.2056513315502992, 1.071397699534742 }, + { 4.698703675388435, -0.7957304428932623, -4.7711315599977855, 0.09057617743728708, + 1.4691381287521628, -0.30099768897637336, -2.8594113121092493, 2.9571503367352006 }, + { 1.729635373900504, 1.4043331763077305, 0.4195010449027432, 2.9588786679014305, + -0.579121181023122, -2.422844062570481, -0.8625209856866667, 0.0362011748846961 }, + }, + }, + { + { + { 1.8880869312050084, 0.17014705148566178 }, + { 0.1511761270942118, 1.1057152930560752 }, + }, + { + { 1.3593048698788563, -0.5220741523649461 }, + { 0.1997741131437576, -0.6596574766514716 }, + }, + }, + { + { + { 0.5726217776053402, -0.3670970767855093 }, + { 0.09861077665339368, -0.05460772804233427 }, + }, + { + { 1.104719693762091, -0.9911964637382618 }, + { -0.35545870684141456, 0.4386470014173758 }, + }, + } + + }; + + @Test + public void testFft2dWithGeneratedData() { + for (int testIndex = 0; testIndex < reInputs.length; testIndex++) { + double[] re = reInputs[testIndex]; + double[] im = imInputs[testIndex]; + double[][][] expected = expectedOutputs[testIndex]; + + int sideLength = (int) Math.sqrt(re.length); + + // Your FFT implementation + fft(re, im, sideLength, sideLength); + + // Assert results + for (int i = 0; i < sideLength; i++) { + for (int j = 0; j < sideLength; j++) { + assertComplexEquals("Mismatch at [" + i + "][" + j + "]", + expected[0][i][j], expected[1][i][j], + re[i * sideLength + j], im[i * sideLength + j]); + } + } + } + } + + @Test + public void testIfft2dWithGeneratedData() { + for (int testIndex = 0; testIndex < reInputs.length; testIndex++) { + double[] re = reInputs[testIndex]; + double[] im = imInputs[testIndex]; + double[][][] expected = expectedOutputs[testIndex]; + + int sideLength = (int) Math.sqrt(re.length); + + // Your IFFT implementation + ifft(re, im, sideLength, sideLength); + + // Assert results + for (int i = 0; i < sideLength; i++) { + for (int j = 0; j < sideLength; j++) { + assertComplexEquals("Mismatch at [" + i + "][" + j + "]", + expected[0][i][j], expected[1][i][j], + re[i * sideLength + j], im[i * sideLength + j]); + } + } + } + } +} diff --git a/src/test/java/org/apache/sysds/test/component/matrix/FourierTestWithFiles.java b/src/test/java/org/apache/sysds/test/component/matrix/FourierTestWithFiles.java deleted file mode 100644 index fe051e2c272..00000000000 --- a/src/test/java/org/apache/sysds/test/component/matrix/FourierTestWithFiles.java +++ /dev/null @@ -1,453 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package org.apache.sysds.test.component.matrix; - -import org.junit.Test; -import java.io.IOException; -import java.io.BufferedReader; -import java.io.FileReader; - -import static org.junit.Assert.assertTrue; -import static org.apache.sysds.runtime.matrix.data.LibMatrixFourier.fft; -import static org.apache.sysds.runtime.matrix.data.LibMatrixFourier.ifft; - -public class FourierTestWithFiles { - int progressInterval = 5000; - - // prior to executing the following tests it is necessary to run the Numpy Script in FourierTestData.py - // and add the generated files to the root of the project. - @Test - public void testFftWithNumpyData() throws IOException { - - String filename = "fft_data.csv"; // Path to your CSV file - String path = "./src/test/java/org/apache/sysds/test/component/matrix/"; - - BufferedReader reader = new BufferedReader(new FileReader(path+filename)); - String line; - int lineNumber = 0; - long totalTime = 0; // Total time for all FFT computations - int numCalculations = 0; // Number of FFT computations - - while ((line = reader.readLine()) != null) { - lineNumber++; - - String[] values = line.split(","); - int n = values.length / 3; - - double[] re = new double[n]; - double[] im = new double[n]; - - double[][] expected = new double[2][n]; // First row for real, second row for imaginary parts - - for (int i = 0; i < n; i++) { - re[i] = Double.parseDouble(values[i]); - expected[0][i] = Double.parseDouble(values[n + i]); // Real part - expected[1][i] = Double.parseDouble(values[n * 2 + i]); // Imaginary part - } - - long startTime = System.nanoTime(); - fft(re, im, 1, n); - long endTime = System.nanoTime(); - - if(lineNumber > 1000){ - totalTime += (endTime - startTime); - numCalculations++; - - if (numCalculations % progressInterval == 0) { - double averageTime = (totalTime / 1e6) / numCalculations; // Average time in milliseconds - System.out.println("fft(double[][][] in): Average execution time after " + numCalculations + " calculations: " + String.format("%.8f", averageTime/1000) + " s"); - } - } - - double[][] actual = {re, im}; - - // Validate the FFT results - validateFftResults(expected, actual, lineNumber); - } - - reader.close(); - } - - private void validateFftResults(double[][] expected, double[][] actual, int lineNumber) { - - int length = expected[0].length; - - for (int i = 0; i < length; i++) { - double realActual = actual[0][i]; - double imagActual = actual[1][i]; - assertEquals("Mismatch in real part at index " + i + " in line " + lineNumber, expected[0][i], realActual, 1e-9); - assertEquals("Mismatch in imaginary part at index " + i + " in line " + lineNumber, expected[1][i], imagActual, 1e-9); - } - - if(lineNumber % progressInterval == 0){ - System.out.println("fft(double[][][] in): Finished processing line " + lineNumber); - } - - } - - @Test - public void testFftExecutionTime() throws IOException { - - String filename = "fft_data.csv"; // Path to your CSV file - String path = "./src/test/java/org/apache/sysds/test/component/matrix/"; - - BufferedReader reader = new BufferedReader(new FileReader(path+filename)); - String line; - int lineNumber = 0; - long totalTime = 0; // Total time for all FFT computations - int numCalculations = 0; // Number of FFT computations - - while ((line = reader.readLine()) != null) { - - lineNumber++; - String[] values = line.split(","); - int n = values.length / 3; - - double[] re = new double[n]; - double[] im = new double[n]; - - for (int i = 0; i < n; i++) { - re[i] = Double.parseDouble(values[i]); // Real part - im[i] = Double.parseDouble(values[n + i]); // Imaginary part - } - - long startTime = System.nanoTime(); - - fft(re, im, 1, n); - - long endTime = System.nanoTime(); - - if(lineNumber > 1000){ - totalTime += (endTime - startTime); - numCalculations++; - - if (numCalculations % progressInterval == 0) { - double averageTime = (totalTime / 1e6) / numCalculations; // Average time in milliseconds - System.out.println("fft_old(double[][][] in, boolean calcInv) Average execution time after " + numCalculations + " calculations: " + String.format("%.8f", averageTime/1000) + " s"); - } - } - } - - reader.close(); - } - - @Test - public void testFftExecutionTimeOfOneDimFFT() throws IOException { - - String filename = "fft_data.csv"; // Path to your CSV file - String path = "./src/test/java/org/apache/sysds/test/component/matrix/"; - - BufferedReader reader = new BufferedReader(new FileReader(path+filename)); - String line; - int lineNumber = 0; - long totalTime = 0; // Total time for all FFT computations - int numCalculations = 0; // Number of FFT computations - - while ((line = reader.readLine()) != null) { - - lineNumber++; - String[] values = line.split(","); - int n = values.length / 2; - - double[] re = new double[n]; - double[] im = new double[n]; - - for (int i = 0; i < n; i++) { - re[i] = Double.parseDouble(values[i]); // Real part - im[i] = Double.parseDouble(values[n + i]); // Imaginary part - } - - long startTime = System.nanoTime(); - // one dimensional - fft(re, im, 1, n); - - long endTime = System.nanoTime(); - - if(lineNumber > 1000){ - totalTime += (endTime - startTime); - numCalculations++; - - if (numCalculations % progressInterval == 0) { - double averageTime = (totalTime / 1e6) / numCalculations; // Average time in milliseconds - System.out.println("fft_one_dim: Average execution time after " + numCalculations + " calculations: " + String.format("%.8f", averageTime/1000) + " s "); - } - } - } - - reader.close(); - } - - // prior to executing this test it is necessary to run the Numpy Script in FourierTestData.py and add the generated file to the root of the project. - @Test - public void testIfftWithRealNumpyData() throws IOException { - - String filename = "ifft_data.csv"; // Path to your CSV file - String path = "./src/test/java/org/apache/sysds/test/component/matrix/"; - - BufferedReader reader = new BufferedReader(new FileReader(path+filename)); - String line; - int lineNumber = 0; - - while ((line = reader.readLine()) != null) { - - lineNumber++; - String[] values = line.split(","); - int n = values.length / 3; - - double[] re = new double[n]; - double[] im = new double[n]; - - double[][] expected = new double[2][n]; // First row for real, second row for imaginary parts - - for (int i = 0; i < n; i++) { - re[i] = Double.parseDouble(values[i]); // Real part of input - // Imaginary part of input is assumed to be 0 - expected[0][i] = Double.parseDouble(values[n + i]); // Real part of expected output - expected[1][i] = Double.parseDouble(values[n * 2 + i]); // Imaginary part of expected output - } - - ifft(re, im, 1, n); // Perform IFFT - - double[][] actual = new double[][]{re, im}; - // Validate the IFFT results - validateFftResults(expected, actual, lineNumber); - } - - reader.close(); - - } - - @Test - public void testIfftWithComplexNumpyData() throws IOException { - - String filename = "complex_ifft_data.csv"; // Adjusted path to your IFFT data file with complex inputs - String path = "./src/test/java/org/apache/sysds/test/component/matrix/"; - - BufferedReader reader = new BufferedReader(new FileReader(path+filename)); - String line; - int lineNumber = 0; - long totalTime = 0; // Total time for all IFFT computations - int numCalculations = 0; // Number of IFFT computations - - while ((line = reader.readLine()) != null) { - lineNumber++; - String[] values = line.split(","); - int n = values.length / 4; // Adjusted for complex numbers - - // Real and imaginary parts - double[] re = new double[n]; - double[] im = new double[n]; - - double[][] expected = new double[2][n]; // Expected real and imaginary parts - - for (int i = 0; i < n; i++) { - re[i] = Double.parseDouble(values[i]); // Real part of input - im[i] = Double.parseDouble(values[i + n]); // Imaginary part of input - expected[0][i] = Double.parseDouble(values[i + 2 * n]); // Expected real part - expected[1][i] = Double.parseDouble(values[i + 3 * n]); // Expected imaginary part - } - - long startTime = System.nanoTime(); - - ifft(re, im, 1, n); // Perform IFFT - - long endTime = System.nanoTime(); - - if (lineNumber > 1000) { - totalTime += (endTime - startTime); - numCalculations++; - } - - double[][] actual = new double[][]{re, im}; - // Validate the IFFT results - validateComplexIFftResults(expected, actual, lineNumber); - - if (lineNumber % progressInterval == 0) { - double averageTime = (totalTime / 1e6) / numCalculations; // Average time in milliseconds - System.out.println("ifft: Average execution time after " + numCalculations + " calculations: " + String.format("%.8f", averageTime / 1000) + " s"); - } - } - - reader.close(); - } - - private void validateComplexIFftResults(double[][] expected, double[][] actual, int lineNumber) { - - int length = expected[0].length; - - for (int i = 0; i < length; i++) { - double realActual = actual[0][i]; - double imagActual = actual[1][i]; - assertEquals("Mismatch in real part at index " + i + " in line " + lineNumber, expected[0][i], realActual, 1e-9); - assertEquals("Mismatch in imaginary part at index " + i + " in line " + lineNumber, expected[1][i], imagActual, 1e-9); - } - - if (lineNumber % progressInterval == 0) { - System.out.println("ifft(complex input): Finished processing line " + lineNumber); - } - - } - - @Test - public void testFft2dWithNumpyData() throws IOException { - - String filename = "complex_fft_2d_data.csv"; // path to your 2D FFT data file - String path = "./src/test/java/org/apache/sysds/test/component/matrix/"; - - BufferedReader reader = new BufferedReader(new FileReader(path+filename)); - String line; - int lineNumber = 0; - long totalTime = 0; // Total time for all FFT 2D computations - int numCalculations = 0; // Number of FFT 2D computations - - while ((line = reader.readLine()) != null) { - lineNumber++; - String[] values = line.split(","); - int halfLength = values.length / 4; - int sideLength = (int) Math.sqrt(halfLength); // Assuming square matrix - - // Real and imaginary parts - double[] re = new double[sideLength*sideLength]; - double[] im = new double[sideLength*sideLength]; - - double[][][] expected = new double[2][sideLength][sideLength]; - - for (int i = 0; i < halfLength; i++) { - int row = i / sideLength; - int col = i % sideLength; - - // i == row*sideLength+col?! - re[row*sideLength+col] = Double.parseDouble(values[i]); - im[row*sideLength+col] = Double.parseDouble(values[i + halfLength]); - expected[0][row][col] = Double.parseDouble(values[i + 2 * halfLength]); - expected[1][row][col] = Double.parseDouble(values[i + 3 * halfLength]); - } - - long startTime = System.nanoTime(); - // Use your fft2d implementation - fft(re, im, sideLength, sideLength); - //double[][][] javaFftResult = fft_old(input, false); // Use your fft2d implementation - long endTime = System.nanoTime(); - totalTime += (endTime - startTime); - numCalculations++; - - for (int i = 0; i < sideLength; i++) { - for (int j = 0; j < sideLength; j++) { - assertComplexEquals("Mismatch at [" + i + "][" + j + "] in line " + lineNumber, - expected[0][i][j], expected[1][i][j], - re[i*sideLength+j], im[i*sideLength+j]); - } - } - - if (lineNumber % progressInterval == 0) { - double averageTime = (totalTime / 1e6) / numCalculations; // Average time in milliseconds - System.out.println("fft2d: Average execution time after " + numCalculations + " calculations: " + String.format("%.8f", averageTime/1000) + " s"); - } - } - - reader.close(); - System.out.println("fft2d: Finished processing " + lineNumber + " lines.\n Average execution time: " + String.format("%.8f", (totalTime / 1e6 / numCalculations)/1000) + " s"); - - } - - - @Test - public void testIfft2dWithNumpyData() throws IOException { - - String filename = "complex_ifft_2d_data.csv"; // path to your 2D IFFT data file - String path = "./src/test/java/org/apache/sysds/test/component/matrix/"; - - BufferedReader reader = new BufferedReader(new FileReader(path+filename)); - String line; - int lineNumber = 0; - long totalTime = 0; // Total time for all IFFT 2D computations - int numCalculations = 0; // Number of IFFT 2D computations - - while ((line = reader.readLine()) != null) { - - lineNumber++; - String[] values = line.split(","); - int halfLength = values.length / 4; - int sideLength = (int) Math.sqrt(halfLength); // Assuming square matrix - - // Real and imaginary parts - double[] re = new double[sideLength*sideLength]; - double[] im = new double[sideLength*sideLength]; - - double[][][] expected = new double[2][sideLength][sideLength]; - - for (int i = 0; i < halfLength; i++) { - int row = i / sideLength; - int col = i % sideLength; - - re[row*sideLength+col] = Double.parseDouble(values[i]); - im[row*sideLength+col] = Double.parseDouble(values[i + halfLength]); - - expected[0][row][col] = Double.parseDouble(values[i + 2 * halfLength]); - expected[1][row][col] = Double.parseDouble(values[i + 3 * halfLength]); - } - - long startTime = System.nanoTime(); - - // Use your ifft2d implementation - ifft(re, im, sideLength, sideLength); - - long endTime = System.nanoTime(); - if(lineNumber > 1000){ - totalTime += (endTime - startTime); - numCalculations++; - } - - for (int i = 0; i < sideLength; i++) { - for (int j = 0; j < sideLength; j++) { - assertComplexEquals("Mismatch at [" + i + "][" + j + "] in line " + lineNumber, - expected[0][i][j], expected[1][i][j], - re[i*sideLength+j], im[i*sideLength+j]); - } - } - - if (lineNumber % progressInterval == 0) { - System.out.println("fft2d/ifft2d: Finished processing line " + lineNumber); - double averageTime = (totalTime / 1e6) / numCalculations; // Average time in milliseconds - System.out.println("Ifft2d Average execution time after " + numCalculations + " calculations: " + String.format("%.8f", averageTime/1000) + " s"); - } - } - - reader.close(); - System.out.println("ifft2d: Finished processing " + lineNumber + " lines.\n Average execution time: " + String.format("%.8f", (totalTime / 1e6 / numCalculations)/1000) + " ms"); - - } - - // Helper method for asserting equality with a tolerance - private static void assertEquals(String message, double expected, double actual, double tolerance) { - assertTrue(message + " - Expected: " + expected + ", Actual: " + actual, Math.abs(expected - actual) <= tolerance); - } - - private void assertComplexEquals(String message, double expectedReal, double expectedImag, double actualReal, double actualImag) { - - final double EPSILON = 1e-9; - assertTrue(message + " - Mismatch in real part. Expected: " + expectedReal + ", Actual: " + actualReal, - Math.abs(expectedReal - actualReal) <= EPSILON); - assertTrue(message + " - Mismatch in imaginary part. Expected: " + expectedImag + ", Actual: " + actualImag, - Math.abs(expectedImag - actualImag) <= EPSILON); - - } - -} From b063424600c436c4311e184692b6702ea897cc77 Mon Sep 17 00:00:00 2001 From: Jessica Priebe Date: Sat, 20 Jan 2024 16:38:05 +0100 Subject: [PATCH 042/133] added tests --- .../test/component/matrix/FourierTest.java | 97 +++++++++++++++++++ 1 file changed, 97 insertions(+) diff --git a/src/test/java/org/apache/sysds/test/component/matrix/FourierTest.java b/src/test/java/org/apache/sysds/test/component/matrix/FourierTest.java index bdd35f45206..e687b525eac 100644 --- a/src/test/java/org/apache/sysds/test/component/matrix/FourierTest.java +++ b/src/test/java/org/apache/sysds/test/component/matrix/FourierTest.java @@ -158,4 +158,101 @@ public void test_fft_two_dim_matrixBlock_row_2() { } + @Test + public void failed_test_ifft_with_real_numpy_data() { + + // removed 0's at the end + MatrixBlock re = new MatrixBlock(1, 16, new double[]{ + 0.5398705320215192, 0.1793355360736929, 0.9065254044489506, 0.45004385530909075, + 0.11128090341119468, 0.11742862805303522, 0.7574827475752481, 0.2778193170985158, + 0.9251562928110273, 0.9414429667551927, 0.45131569795507087, 0.9522067687409731, + 0.22491032260636257, 0.6579426733967295, 0.7021558730366062, 0.7861117825617701, + }); + + MatrixBlock im = new MatrixBlock(1, 16, new double[16]); + + double[] expected_re = { + 0.5613143313659362, -0.020146500453061336, 0.07086545895481336, -0.05003801442765281, + -0.06351635451036074, -0.03346768844048936, 0.07023899089706032, 0.007330763123826495, + 0.016022890367311193, 0.007330763123826495, 0.07023899089706032, -0.03346768844048936, + -0.06351635451036074, -0.05003801442765281, 0.07086545895481336, -0.020146500453061336 + }; + + double[] expected_im = { + 0.0, -0.07513090216687965, 0.023854392878864396, -0.018752997939582024, + -0.035626994964481226, -0.07808216015046443, 0.036579082654843526, -0.10605270957897009, + 0.0, 0.10605270957897009, -0.036579082654843526, 0.07808216015046443, + 0.035626994964481226, 0.018752997939582024, -0.023854392878864396, 0.07513090216687965 + }; + + MatrixBlock[] res = ifft(re, im); + + assertArrayEquals(expected_re, res[0].getDenseBlockValues(), 0.0001); + assertArrayEquals(expected_im, res[1].getDenseBlockValues(), 0.0001); + + } + + @Test + public void failed_test_ifft_2d_with_generated_data() { + + // TODO: + + MatrixBlock re = new MatrixBlock(2, 2, new double[]{ + 0.6749989259154331, 0.6278845555308362, 0.995916990652601, 0.511472971081564 + }); + + MatrixBlock im = new MatrixBlock(2, 2, new double[]{ + 0.8330832079105173, 0.09857986129294982, 0.6808883894146879, 0.28353782431047303 + }); + + double[] expected_re = { + 2.81027344, 0.53155839, -0.20450648, -0.43732965 + }; + + double[] expected_im = { + 1.89608928, 1.13185391, -0.03276314, 0.33715278 + }; + + MatrixBlock[] res = ifft(re, im); + + assertArrayEquals(expected_re, res[0].getDenseBlockValues(), 0.0001); + assertArrayEquals(expected_im, res[1].getDenseBlockValues(), 0.0001); + + } + + @Test + public void failed_test_ifft_with_complex_numpy_data() { + + MatrixBlock re = new MatrixBlock(1, 16, new double[]{ + 0.17768499045697306, 0.3405035491673728, 0.9272906450602005, 0.28247504052271843, + 0.42775517613102865, 0.8338783039818357, 0.9813749624557385, 0.47224489612381737, + 0.7936831995784907, 0.5584182145651306, 0.5296113722056018, 0.6687593295928902, + 0.9630598447622862, 0.7130539473424196, 0.860081483892192, 0.8985058305053549 + }); + + MatrixBlock im = new MatrixBlock(1, 16, new double[16]); + + // adjusted the expected output + double[] expected_re = { + 0.6517738, -0.0263837 , -0.03631354, -0.01644966, + -0.05851095, -0.0849794, -0.01611732, -0.02618679, + 0.05579391, -0.02618679, -0.01611732, -0.0849794, + -0.05851095, -0.01644966, -0.03631354, -0.0263837 + }; + + double[] expected_im = { + 0, -0.04125649, -0.07121312, 0.02554502, + 0.00774181, -0.08723921, -0.02314382, -0.02021455, + 0, 0.02021455, 0.02314382, 0.08723921, + -0.00774181, -0.02554502, 0.07121312, 0.04125649 + + }; + + MatrixBlock[] res = ifft(re, im); + + assertArrayEquals(expected_re, res[0].getDenseBlockValues(), 0.0001); + assertArrayEquals(expected_im, res[1].getDenseBlockValues(), 0.0001); + + } + } From 1f46e22a25463a269a7b6b253c271d1a5bcf619a Mon Sep 17 00:00:00 2001 From: Jessica Priebe Date: Sat, 20 Jan 2024 16:44:06 +0100 Subject: [PATCH 043/133] updated tests --- .../apache/sysds/test/component/matrix/FourierTest.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/test/java/org/apache/sysds/test/component/matrix/FourierTest.java b/src/test/java/org/apache/sysds/test/component/matrix/FourierTest.java index e687b525eac..65108008900 100644 --- a/src/test/java/org/apache/sysds/test/component/matrix/FourierTest.java +++ b/src/test/java/org/apache/sysds/test/component/matrix/FourierTest.java @@ -195,8 +195,6 @@ public void failed_test_ifft_with_real_numpy_data() { @Test public void failed_test_ifft_2d_with_generated_data() { - // TODO: - MatrixBlock re = new MatrixBlock(2, 2, new double[]{ 0.6749989259154331, 0.6278845555308362, 0.995916990652601, 0.511472971081564 }); @@ -205,12 +203,13 @@ public void failed_test_ifft_2d_with_generated_data() { 0.8330832079105173, 0.09857986129294982, 0.6808883894146879, 0.28353782431047303 }); + // adjusted the expected output double[] expected_re = { - 2.81027344, 0.53155839, -0.20450648, -0.43732965 + 0.70256836, 0.1328896, -0.05112662, -0.10933241 }; double[] expected_im = { - 1.89608928, 1.13185391, -0.03276314, 0.33715278 + 0.47402232, 0.28296348, -0.00819079, 0.0842882 }; MatrixBlock[] res = ifft(re, im); From c6bf52d4c47f7de2073f7ac0e124d56ada3beaa7 Mon Sep 17 00:00:00 2001 From: Jessica Priebe Date: Sun, 21 Jan 2024 22:54:56 +0100 Subject: [PATCH 044/133] iterative implementations --- .../runtime/matrix/data/LibMatrixFourier.java | 82 +++++++++------- .../test/component/matrix/FourierTest.java | 96 ++++++++++++++++++- 2 files changed, 141 insertions(+), 37 deletions(-) diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java index df5b7337ddc..2c9fd8339dd 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java @@ -79,11 +79,11 @@ public static void fft(double[] re, double[] im, int rows, int cols) { double[] im_inter = new double[rows*cols]; for(int i = 0; i < rows; i++){ - fft_one_dim(re, im, re_inter, im_inter, i*cols, 1, cols, cols); + fft_one_dim(re, im, re_inter, im_inter, i*cols, (i+1)*cols, cols, 1); } for(int j = 0; j < cols; j++){ - fft_one_dim(re, im, re_inter, im_inter, j, cols, rows, rows*cols); + fft_one_dim(re, im, re_inter, im_inter, j, j+rows*cols, rows, cols); } } @@ -104,64 +104,82 @@ public static void ifft(double[] re, double[] im, int rows, int cols) { double[] im_inter = new double[rows*cols]; for(int j = 0; j < cols; j++){ - ifft_one_dim(re, im, re_inter, im_inter, j, cols, rows, rows*cols); + ifft_one_dim(re, im, re_inter, im_inter, j, j+rows*cols, rows, cols); + } for(int i = 0; i < rows; i++){ - ifft_one_dim(re, im, re_inter, im_inter, i*cols, 1, cols, cols); + ifft_one_dim(re, im, re_inter, im_inter, i*cols, (i+1)*cols, cols, 1); } } - private static void fft_one_dim(double[] re, double[] im, double[] re_inter, double[] im_inter, int start, int step, int num, int subArraySize) { + public static void fft_one_dim(double[] re, double[] im, double[] re_inter, double[] im_inter, int start, int stop, int num, int minStep) { + // start inclusive, stop exclusive if(num == 1) return; - double angle = -2*FastMath.PI/num; - // fft for even indices - fft_one_dim(re, im, re_inter, im_inter, start, step*2, num/2, subArraySize); - // fft for odd indices - fft_one_dim(re, im, re_inter, im_inter,start+step, step*2, num/2, subArraySize); + // even indices + for(int step = minStep*(num/2), subNum = 2; subNum <= num; step/=2, subNum*=2){ - // iterates over even indices - for(int j = start, cnt = 0; cnt < num/2; j+=(2*step), cnt++){ + double angle = -2*FastMath.PI/subNum; - double omega_pow_re = FastMath.cos(cnt*angle); - double omega_pow_im = FastMath.sin(cnt*angle); + // use ceil for the main (sub)array + for(int sub = 0; sub < FastMath.ceil(num/(2*(double)subNum)); sub++){ - // calculate m using the result of odd index - double m_re = omega_pow_re * re[j+step] - omega_pow_im * im[j+step]; - double m_im = omega_pow_re * im[j+step] + omega_pow_im * re[j+step]; + for(int isOdd = 0; isOdd < 2; isOdd++) { - int index = start+cnt*step; - re_inter[index] = re[j] + m_re; - re_inter[index+subArraySize/2] = re[j] - m_re; + // no odd values for main (sub)array + if (isOdd == 1 && subNum == num) return; - im_inter[index] = im[j] + m_im; - im_inter[index+subArraySize/2] = im[j] - m_im; - } + int startSub = start + sub*minStep + isOdd*(step/2); + + // first iterates over even indices, then over odd indices + for (int j = startSub, cnt = 0; cnt < subNum / 2; j += 2*step, cnt++) { + + double omega_pow_re = FastMath.cos(cnt * angle); + double omega_pow_im = FastMath.sin(cnt * angle); + + // calculate m using the result of odd index + double m_re = omega_pow_re * re[j + step] - omega_pow_im * im[j + step]; + double m_im = omega_pow_re * im[j + step] + omega_pow_im * re[j + step]; + + int index = startSub + cnt * step; + re_inter[index] = re[j] + m_re; + re_inter[index + (stop-start)/2] = re[j] - m_re; + + im_inter[index] = im[j] + m_im; + im_inter[index + (stop-start)/2] = im[j] - m_im; + } + + for (int j = startSub; j < startSub + (stop-start); j += step) { + re[j] = re_inter[j]; + im[j] = im_inter[j]; + re_inter[j] = 0; + im_inter[j] = 0; + } + + } + + } - for(int j = start; j < start+subArraySize; j+=step){ - re[j] = re_inter[j]; - im[j] = im_inter[j]; - re_inter[j] = 0; - im_inter[j] = 0; } } - private static void ifft_one_dim(double[] re, double[] im, double[] re_inter, double[] im_inter, int start, int step, int num, int subArraySize) { + private static void ifft_one_dim(double[] re, double[] im, double[] re_inter, double[] im_inter, int start, int stop, int num, int minStep) { // conjugate input - for (int i = start; i < start+num*step; i+=step){ + for (int i = start; i < start+num*minStep; i+=minStep){ im[i] = -im[i]; } // apply fft - fft_one_dim(re, im, re_inter, im_inter, start, step, num, subArraySize); + //fft_one_dim_recursive(re, im, re_inter, im_inter, start, step, num, subArraySize); + fft_one_dim(re, im, re_inter, im_inter, start, stop, num, minStep); // conjugate and scale result - for (int i = start; i < start+num*step; i+=step){ + for (int i = start; i < start+num*minStep; i+=minStep){ re[i] = re[i]/num; im[i] = -im[i]/num; } diff --git a/src/test/java/org/apache/sysds/test/component/matrix/FourierTest.java b/src/test/java/org/apache/sysds/test/component/matrix/FourierTest.java index 65108008900..2b25b54c7e5 100644 --- a/src/test/java/org/apache/sysds/test/component/matrix/FourierTest.java +++ b/src/test/java/org/apache/sysds/test/component/matrix/FourierTest.java @@ -26,7 +26,6 @@ import static org.apache.sysds.runtime.matrix.data.LibMatrixFourier.ifft; import static org.junit.Assert.assertArrayEquals; -import static org.junit.Assert.assertTrue; public class FourierTest { @@ -159,9 +158,9 @@ public void test_fft_two_dim_matrixBlock_row_2() { } @Test - public void failed_test_ifft_with_real_numpy_data() { + public void test_ifft_with_complex_numpy_data() { - // removed 0's at the end + // removed 0's at the end, not just real MatrixBlock re = new MatrixBlock(1, 16, new double[]{ 0.5398705320215192, 0.1793355360736929, 0.9065254044489506, 0.45004385530909075, 0.11128090341119468, 0.11742862805303522, 0.7574827475752481, 0.2778193170985158, @@ -193,7 +192,7 @@ public void failed_test_ifft_with_real_numpy_data() { } @Test - public void failed_test_ifft_2d_with_generated_data() { + public void test_ifft_2d_with_generated_data() { MatrixBlock re = new MatrixBlock(2, 2, new double[]{ 0.6749989259154331, 0.6278845555308362, 0.995916990652601, 0.511472971081564 @@ -220,8 +219,9 @@ public void failed_test_ifft_2d_with_generated_data() { } @Test - public void failed_test_ifft_with_complex_numpy_data() { + public void test_ifft_with_real_numpy_data() { + // not complex MatrixBlock re = new MatrixBlock(1, 16, new double[]{ 0.17768499045697306, 0.3405035491673728, 0.9272906450602005, 0.28247504052271843, 0.42775517613102865, 0.8338783039818357, 0.9813749624557385, 0.47224489612381737, @@ -254,4 +254,90 @@ public void failed_test_ifft_with_complex_numpy_data() { } + @Test + public void test_fft_two_dim_8_times_8() { + + MatrixBlock re = new MatrixBlock(8, 8, new double[]{ + 0.8435874964408077, 0.3565209485970835, 0.6221038572251737, 0.05712418097055716, + 0.9301368966310067, 0.7748052735242277, 0.21117129518682443, 0.08407931152930459, + 0.5861235649815163, 0.45860122035396356, 0.6647476180103304, 0.9167930424492593, + 0.6310726270028377, 0.11110504251770592, 0.32369996452324756, 0.5790548902504138, + 0.5712310851880162, 0.5967356161025353, 0.6441861776319489, 0.14402445187596158, + 0.22642623625293545, 0.922443731897705, 0.9527667119829785, 0.2250880965427453, + 0.5755375055168817, 0.48898427237526954, 0.24518238824389693, 0.832292384016089, + 0.23789083930394805, 0.5558982102157535, 0.7220016080026206, 0.9666747522359772, + 0.20509423975210916, 0.23170117015755587, 0.7141206714718693, 0.2014158450611332, + 0.6486924358372994, 0.9044990419216931, 0.19849364935627056, 0.23340297110822106, + 0.46854050631969246, 0.10134155509558795, 0.5563200388698989, 0.2669820016661475, + 0.8889445005077763, 0.4273462470993935, 0.8269490075576963, 0.044351336481537995, + 0.3771564738915597, 0.11333723996854606, 0.6913138435759023, 0.062431275099310124, + 0.8003013976959878, 0.1276686539064662, 0.975167392001707, 0.44595301043682656, + 0.18401328301977316, 0.7158585484384759, 0.3240126702723025, 0.740836665073052, + 0.8890279623888511, 0.8841266040978419, 0.3058930798936259, 0.8987579873722049 + }); + + MatrixBlock im = new MatrixBlock(1, 16, new double[]{ + 0.8572457113722648, 0.668182795310341, 0.9739416721141464, 0.8189153345383146, + 0.6425950286263254, 0.3569634253534639, 0.19715070300424575, 0.8915344479242211, + 0.39207930659031054, 0.1625193685179268, 0.2523438052868171, 0.30940628850519547, + 0.7461468672112159, 0.7123766750132684, 0.5261041429273977, 0.867155304805022, + 0.7207769261821749, 0.9139070611733158, 0.7638265842242135, 0.3508092733308539, + 0.6075639148195967, 0.9615531048215422, 0.719499617407839, 0.9616615941848492, + 0.2667126256574347, 0.8215093145949468, 0.4240476512138287, 0.5015798652459079, + 0.19784651066995873, 0.42315603332105356, 0.5575575283922164, 0.9051304828282485, + 0.30117855478511435, 0.14219967492505514, 0.32675429179906557, 0.04889894374947912, + 0.8338579676700041, 0.370201089804747, 0.06025987717830994, 0.9407970353033787, + 0.9871788482561391, 0.75984297199074, 0.414969247979073, 0.2453785474698862, + 0.06295683447294731, 0.40141192931768566, 0.19520663793867488, 0.3179027928938928, + 0.591138083168947, 0.5318366162549014, 0.04865894304644136, 0.5339043989658795, + 0.09892519435896363, 0.31616794516128466, 0.06702286400447643, 0.8466767273121609, + 0.8134875055724791, 0.6232554321597641, 0.21208039111457444, 0.25629831822305926, + 0.7373140896724466, 0.020486629088602437, 0.8666668269441752, 0.20094387974200512 + }); + + double[] expected_re = { + 32.51214260297584, -3.4732779237490314, -0.7257760912890102, -1.9627494786611792, + 3.571671446098747, 1.0451692206901078, 0.8970702451384204, -1.3739767803210428, + 4.892442103095981, -1.1656855109832338, -0.5854908742291178, -1.3497699084098418, + -1.377003693155216, -2.1698030461214923, 0.8172129683973663, -0.9259076518379679, + -1.1343756245445045, -1.8734967800709579, 1.7367517585478862, 0.07349671655414491, + -1.5933768052439223, 2.7965196291943983, 4.292588673604611, -1.1032899622026413, + -2.4643093702874834, 2.109128987930992, 3.2834030498896456, 0.21371926254596152, + -0.3107488550365316, 0.7293395030253796, -2.542403789759091, -1.8570654162590052, + -2.325781245331303, 0.7963395911053484, -2.351990667205867, -2.4241188304485735, + 4.689766636746301, 3.4748121457306116, 0.5539071663846459, -0.950099313504134, + 2.122310975524349, 4.527637759721644, -2.125596093625001, 1.7676565539001592, + 5.748332643019926, 0.860140632830907, 2.9735142186218484, -1.7198774815194848, + -0.18418859401548549, 1.1909629561342188, 0.21710627714418418, -1.5537184277268996, + -0.5486540814747869, 0.14807346060743987, 2.4333154010438087, -3.2930077637380393, + -2.3820067665775113, 2.5463581304688057, 2.5927580716559615, 1.8921802492721915, + 0.4957713559465988, -0.4983536537999108, 3.5808175362367676, 0.7530823235547575 + }; + + double[] expected_im = { + 32.64565805549281, 5.177639365468945, 1.1792020104097647, -0.4850627423320939, + -1.719468548169175, -3.064146170894837, 3.3226243586118906, 2.3819341640916107, + 1.5824429804361388, -2.192940882164737, 0.5774407122593543, 0.16873948200103983, + 4.297014293326352, -3.1712082122026883, 0.9741291131305898, -2.4929883795121235, + 1.111763301820595, -1.4012254390671657, -0.33687898317382636, 2.324190267133635, + -2.8862969254091397, -4.7558982401265135, 1.8244587481290004, 0.5310550630270396, + 2.655726742689745, 2.510014260306531, 0.25589537824783704, 1.8720307201415736, + -2.6458046644482884, 2.1732611302115585, -2.5162250969793227, -0.9103444457919911, + 2.2835527482590248, 0.5187392677625127, -3.335253420903965, 1.4668560670097441, + -1.9681585205341436, -2.81914578771063, 4.818094364700921, -0.877636803126361, + 1.803174743823159, 3.1346192487664277, -3.564058675191744, -0.3391381913837902, + -1.2897867384105863, 2.315065426377637, 1.5764817121472765, 2.412894091248795, + -2.3182678917385218, -3.057303547366563, 0.033996764974414395, -0.5825423640666696, + 6.395088232674363, -0.5553659624089358, 1.1079219041153268, 0.1094531803830765, + 3.488182265163636, -1.5698242466218544, -0.1013387045518459, 0.9269290699615746, + -0.699890233104248, 3.617209720991753, -0.5565163478425035, 3.502962737763559 + }; + + MatrixBlock[] res = fft(re, im); + + assertArrayEquals(expected_re, res[0].getDenseBlockValues(), 0.0001); + assertArrayEquals(expected_im, res[1].getDenseBlockValues(), 0.0001); + + } + } From 8f240ebcc9c431030f30c6d2d3102600a07af24b Mon Sep 17 00:00:00 2001 From: Frederic Zoepffel Date: Mon, 22 Jan 2024 00:11:43 +0545 Subject: [PATCH 045/133] integrated 2 inputs for ifft --- .../parser/BuiltinFunctionExpression.java | 3292 +++++++++-------- .../instructions/CPInstructionParser.java | 610 +-- .../instructions/cp/CPInstruction.java | 93 +- .../cp/MultiReturnBuiltinCPInstruction.java | 101 +- ...eturnMatrixMatrixBuiltinCPInstruction.java | 115 + .../runtime/matrix/data/LibCommonsMath.java | 89 +- .../runtime/matrix/data/LibMatrixFourier.java | 124 +- .../propagation/PrivacyPropagator.java | 428 ++- 8 files changed, 2563 insertions(+), 2289 deletions(-) create mode 100644 src/main/java/org/apache/sysds/runtime/instructions/cp/MultiReturnMatrixMatrixBuiltinCPInstruction.java diff --git a/src/main/java/org/apache/sysds/parser/BuiltinFunctionExpression.java b/src/main/java/org/apache/sysds/parser/BuiltinFunctionExpression.java index 275e65975bc..074ca5d3ceb 100644 --- a/src/main/java/org/apache/sysds/parser/BuiltinFunctionExpression.java +++ b/src/main/java/org/apache/sysds/parser/BuiltinFunctionExpression.java @@ -44,12 +44,13 @@ public class BuiltinFunctionExpression extends DataIdentifier { protected Expression[] _args = null; private Builtins _opcode; - public BuiltinFunctionExpression(ParserRuleContext ctx, Builtins bifop, ArrayList args, String fname) { + public BuiltinFunctionExpression(ParserRuleContext ctx, Builtins bifop, ArrayList args, + String fname) { _opcode = bifop; setCtxValuesAndFilename(ctx, fname); args = expandDnnArguments(args); _args = new Expression[args.size()]; - for(int i=0; i < args.size(); i++) { + for (int i = 0; i < args.size(); i++) { _args[i] = args.get(i).getExpr(); } } @@ -66,7 +67,7 @@ public BuiltinFunctionExpression(Builtins bifop, Expression[] args, ParseInfo pa public BuiltinFunctionExpression(ParserRuleContext ctx, Builtins bifop, Expression[] args, String fname) { _opcode = bifop; _args = new Expression[args.length]; - for(int i=0; i < args.length; i++) { + for (int i = 0; i < args.length; i++) { _args[i] = args[i]; } setCtxValuesAndFilename(ctx, fname); @@ -97,19 +98,19 @@ public Expression getSecondExpr() { public Expression getThirdExpr() { return (_args.length >= 3 ? _args[2] : null); } - + public Expression getFourthExpr() { return (_args.length >= 4 ? _args[3] : null); } - + public Expression getFifthExpr() { return (_args.length >= 5 ? _args[4] : null); } - + public Expression getSixthExpr() { return (_args.length >= 6 ? _args[5] : null); } - + public Expression getSeventhExpr() { return (_args.length >= 7 ? _args[6] : null); } @@ -118,27 +119,26 @@ public Expression getEighthExpr() { return (_args.length >= 8 ? _args[7] : null); } - - public Expression[] getAllExpr(){ + public Expression[] getAllExpr() { return _args; } - + public Expression getExpr(int i) { return (_args.length > i ? _args[i] : null); } - + @Override - public void validateExpression(MultiAssignmentStatement stmt, HashMap ids, HashMap constVars, boolean conditional) - { - if (this.getFirstExpr() instanceof FunctionCallIdentifier){ + public void validateExpression(MultiAssignmentStatement stmt, HashMap ids, + HashMap constVars, boolean conditional) { + if (this.getFirstExpr() instanceof FunctionCallIdentifier) { raiseValidateError("UDF function call not supported as parameter to built-in function call", false); } - + this.getFirstExpr().validateExpression(ids, constVars, conditional); - Expression [] expr = getAllExpr(); - if(expr != null && expr.length > 1) { - for(int i = 1; i < expr.length; i++) { - if (expr[i] instanceof FunctionCallIdentifier){ + Expression[] expr = getAllExpr(); + if (expr != null && expr.length > 1) { + for (int i = 1; i < expr.length; i++) { + if (expr[i] instanceof FunctionCallIdentifier) { raiseValidateError("UDF function call not supported as parameter to built-in function call", false); } expr[i].validateExpression(ids, constVars, conditional); @@ -146,383 +146,395 @@ public void validateExpression(MultiAssignmentStatement stmt, HashMap 0 ? - getFirstExpr().getOutput().getDim1() + 1 : -1; - out1.setDataType(DataType.LIST); - out1.setValueType(getFirstExpr().getOutput().getValueType()); - out1.setDimensions(nrow, 1); - out1.setBlocksize(getFirstExpr().getOutput().getBlocksize()); - - // Output2 - list of removed element - out2.setDataType(DataType.LIST); - out2.setValueType(getFirstExpr().getOutput().getValueType()); - out2.setDimensions(1, 1); - out2.setBlocksize(getFirstExpr().getOutput().getBlocksize()); - - break; - } - case SVD: - checkNumParameters(1); - checkMatrixParam(getFirstExpr()); - - long minMN = Math.min(getFirstExpr().getOutput().getDim1(), getFirstExpr().getOutput().getDim2()); - - // setup output properties - DataIdentifier svdOut1 = (DataIdentifier) getOutputs()[0]; - DataIdentifier svdOut2 = (DataIdentifier) getOutputs()[1]; - DataIdentifier svdOut3 = (DataIdentifier) getOutputs()[2]; - - // Output 1 - svdOut1.setDataType(DataType.MATRIX); - svdOut1.setValueType(ValueType.FP64); - svdOut1.setDimensions(getFirstExpr().getOutput().getDim1(), minMN); - svdOut1.setBlocksize(getFirstExpr().getOutput().getBlocksize()); - - // Output 2 - svdOut2.setDataType(DataType.MATRIX); - svdOut2.setValueType(ValueType.FP64); - svdOut2.setDimensions(minMN, minMN); - svdOut2.setBlocksize(getFirstExpr().getOutput().getBlocksize()); - - // Output 3 - svdOut3.setDataType(DataType.MATRIX); - svdOut3.setValueType(ValueType.FP64); - svdOut3.setDimensions(getFirstExpr().getOutput().getDim2(), minMN); - svdOut3.setBlocksize(getFirstExpr().getOutput().getBlocksize()); - - break; - - case COMPRESS: - if(OptimizerUtils.ALLOW_SCRIPT_LEVEL_COMPRESS_COMMAND) { + break; + + } + case IFFT: { Expression expressionTwo = getSecondExpr(); checkNumParameters(getSecondExpr() != null ? 2 : 1); - checkMatrixFrameParam(getFirstExpr()); - if(expressionTwo != null) + checkMatrixParam(getFirstExpr()); + if (expressionTwo != null) checkMatrixParam(getSecondExpr()); - Identifier compressInput1 = getFirstExpr().getOutput(); - // Identifier compressInput2 = getSecondExpr().getOutput(); + // setup output properties + DataIdentifier ifftOut1 = (DataIdentifier) getOutputs()[0]; + DataIdentifier ifftOut2 = (DataIdentifier) getOutputs()[1]; + + // TODO: Add Validation + // if (getFirstExpr().getOutput().getDim2() != 1 || + // getFirstExpr().getOutput().getDim2() != 2) { + // raiseValidateError("Eigen Decomposition can only be done on a square matrix. + // Input matrix is rectangular (rows=" + getFirstExpr().getOutput().getDim1() + + // ", cols="+ getFirstExpr().getOutput().getDim2() +")", conditional); + // } + + // Output1 - ifft Values + ifftOut1.setDataType(DataType.MATRIX); + ifftOut1.setValueType(ValueType.FP64); + ifftOut1.setDimensions(getFirstExpr().getOutput().getDim1(), getFirstExpr().getOutput().getDim2()); + ifftOut1.setBlocksize(getFirstExpr().getOutput().getBlocksize()); + + // Output2 - ifft Vectors + ifftOut2.setDataType(DataType.MATRIX); + ifftOut2.setValueType(ValueType.FP64); + ifftOut2.setDimensions(getFirstExpr().getOutput().getDim1(), getFirstExpr().getOutput().getDim2()); + ifftOut2.setBlocksize(getFirstExpr().getOutput().getBlocksize()); - DataIdentifier compressOutput = (DataIdentifier) getOutputs()[0]; - compressOutput.setDataType(DataType.MATRIX); - compressOutput.setDimensions(compressInput1.getDim1(), compressInput1.getDim2()); - compressOutput.setBlocksize(compressInput1.getBlocksize()); - compressOutput.setValueType(compressInput1.getValueType()); + break; - DataIdentifier metaOutput = (DataIdentifier) getOutputs()[1]; - metaOutput.setDataType(DataType.FRAME); - metaOutput.setDimensions(compressInput1.getDim1(), -1); } - else - raiseValidateError("Compress/DeCompress instruction not allowed in dml script"); - break; + case REMOVE: { + checkNumParameters(2); + checkListParam(getFirstExpr()); - default: //always unconditional - raiseValidateError("Unknown Builtin Function opcode: " + _opcode, false); + // setup output properties + DataIdentifier out1 = (DataIdentifier) getOutputs()[0]; + DataIdentifier out2 = (DataIdentifier) getOutputs()[1]; + + // Output1 - list after removal + long nrow = getFirstExpr().getOutput().getDim1() > 0 ? getFirstExpr().getOutput().getDim1() + 1 : -1; + out1.setDataType(DataType.LIST); + out1.setValueType(getFirstExpr().getOutput().getValueType()); + out1.setDimensions(nrow, 1); + out1.setBlocksize(getFirstExpr().getOutput().getBlocksize()); + + // Output2 - list of removed element + out2.setDataType(DataType.LIST); + out2.setValueType(getFirstExpr().getOutput().getValueType()); + out2.setDimensions(1, 1); + out2.setBlocksize(getFirstExpr().getOutput().getBlocksize()); + + break; + } + case SVD: + checkNumParameters(1); + checkMatrixParam(getFirstExpr()); + + long minMN = Math.min(getFirstExpr().getOutput().getDim1(), getFirstExpr().getOutput().getDim2()); + + // setup output properties + DataIdentifier svdOut1 = (DataIdentifier) getOutputs()[0]; + DataIdentifier svdOut2 = (DataIdentifier) getOutputs()[1]; + DataIdentifier svdOut3 = (DataIdentifier) getOutputs()[2]; + + // Output 1 + svdOut1.setDataType(DataType.MATRIX); + svdOut1.setValueType(ValueType.FP64); + svdOut1.setDimensions(getFirstExpr().getOutput().getDim1(), minMN); + svdOut1.setBlocksize(getFirstExpr().getOutput().getBlocksize()); + + // Output 2 + svdOut2.setDataType(DataType.MATRIX); + svdOut2.setValueType(ValueType.FP64); + svdOut2.setDimensions(minMN, minMN); + svdOut2.setBlocksize(getFirstExpr().getOutput().getBlocksize()); + + // Output 3 + svdOut3.setDataType(DataType.MATRIX); + svdOut3.setValueType(ValueType.FP64); + svdOut3.setDimensions(getFirstExpr().getOutput().getDim2(), minMN); + svdOut3.setBlocksize(getFirstExpr().getOutput().getBlocksize()); + + break; + + case COMPRESS: + if (OptimizerUtils.ALLOW_SCRIPT_LEVEL_COMPRESS_COMMAND) { + Expression expressionTwo = getSecondExpr(); + checkNumParameters(getSecondExpr() != null ? 2 : 1); + checkMatrixFrameParam(getFirstExpr()); + if (expressionTwo != null) + checkMatrixParam(getSecondExpr()); + + Identifier compressInput1 = getFirstExpr().getOutput(); + // Identifier compressInput2 = getSecondExpr().getOutput(); + + DataIdentifier compressOutput = (DataIdentifier) getOutputs()[0]; + compressOutput.setDataType(DataType.MATRIX); + compressOutput.setDimensions(compressInput1.getDim1(), compressInput1.getDim2()); + compressOutput.setBlocksize(compressInput1.getBlocksize()); + compressOutput.setValueType(compressInput1.getValueType()); + + DataIdentifier metaOutput = (DataIdentifier) getOutputs()[1]; + metaOutput.setDataType(DataType.FRAME); + metaOutput.setDimensions(compressInput1.getDim1(), -1); + } else + raiseValidateError("Compress/DeCompress instruction not allowed in dml script"); + break; + + default: // always unconditional + raiseValidateError("Unknown Builtin Function opcode: " + _opcode, false); } } - + private static void setDimensions(DataIdentifier out, Expression exp) { out.setDataType(DataType.MATRIX); out.setValueType(ValueType.FP64); out.setDimensions(exp.getOutput().getDim1(), exp.getOutput().getDim2()); out.setBlocksize(exp.getOutput().getBlocksize()); } - - private static ArrayList orderDnnParams(ArrayList paramExpression, int skip) { + + private static ArrayList orderDnnParams(ArrayList paramExpression, + int skip) { ArrayList newParams = new ArrayList<>(); - for(int i = 0; i < skip; i++) + for (int i = 0; i < skip; i++) newParams.add(paramExpression.get(i)); - String [] orderedParams = { - "stride1", "stride2", "padding1", "padding2", - "input_shape1", "input_shape2", "input_shape3", "input_shape4", - "filter_shape1", "filter_shape2", "filter_shape3", "filter_shape4" + String[] orderedParams = { + "stride1", "stride2", "padding1", "padding2", + "input_shape1", "input_shape2", "input_shape3", "input_shape4", + "filter_shape1", "filter_shape2", "filter_shape3", "filter_shape4" }; - for(int i = 0; i < orderedParams.length; i++) { + for (int i = 0; i < orderedParams.length; i++) { boolean found = false; - for(ParameterExpression param : paramExpression) { - if(param.getName() != null && param.getName().equals(orderedParams[i])) { + for (ParameterExpression param : paramExpression) { + if (param.getName() != null && param.getName().equals(orderedParams[i])) { found = true; newParams.add(param); } } - if(!found) { + if (!found) { throw new LanguageException("Incorrect parameters. Expected " + orderedParams[i] + " to be expanded."); } } @@ -534,74 +546,77 @@ private static ArrayList replaceListParams(ArrayList newParamExpression = new ArrayList<>(); int i = startIndex; - int j = 1; // Assumption: sequential ordering pool_size1, pool_size2 + int j = 1; // Assumption: sequential ordering pool_size1, pool_size2 for (ParameterExpression expr : paramExpression) { - if(expr.getName() != null && expr.getName().equals(inputVarName + j)) { + if (expr.getName() != null && expr.getName().equals(inputVarName + j)) { newParamExpression.add(new ParameterExpression(outputVarName + i, expr.getExpr())); - i++; j++; - } - else { + i++; + j++; + } else { newParamExpression.add(expr); } } return newParamExpression; } - private static ArrayList expandListParams(ArrayList paramExpression, + private static ArrayList expandListParams(ArrayList paramExpression, HashSet paramsToExpand) { ArrayList newParamExpressions = new ArrayList<>(); - for(ParameterExpression expr : paramExpression) { - if(paramsToExpand.contains(expr.getName())) { - if(expr.getExpr() instanceof ExpressionList) { + for (ParameterExpression expr : paramExpression) { + if (paramsToExpand.contains(expr.getName())) { + if (expr.getExpr() instanceof ExpressionList) { int i = 1; - for(Expression e : ((ExpressionList)expr.getExpr()).getValue()) { + for (Expression e : ((ExpressionList) expr.getExpr()).getValue()) { newParamExpressions.add(new ParameterExpression(expr.getName() + i, e)); i++; } } - } - else if(expr.getExpr() instanceof ExpressionList) { - throw new LanguageException("The parameter " + expr.getName() + " cannot be list or is not supported for the given function"); - } - else { + } else if (expr.getExpr() instanceof ExpressionList) { + throw new LanguageException("The parameter " + expr.getName() + + " cannot be list or is not supported for the given function"); + } else { newParamExpressions.add(expr); } } return newParamExpressions; } - + private ArrayList expandDnnArguments(ArrayList paramExpression) { try { - if(_opcode == Builtins.CONV2D || _opcode == Builtins.CONV2D_BACKWARD_FILTER + if (_opcode == Builtins.CONV2D || _opcode == Builtins.CONV2D_BACKWARD_FILTER || _opcode == Builtins.CONV2D_BACKWARD_DATA) { HashSet expand = new HashSet<>(); - expand.add("input_shape"); expand.add("filter_shape"); expand.add("stride"); expand.add("padding"); + expand.add("input_shape"); + expand.add("filter_shape"); + expand.add("stride"); + expand.add("padding"); paramExpression = expandListParams(paramExpression, expand); paramExpression = orderDnnParams(paramExpression, 2); - } - else if(_opcode == Builtins.MAX_POOL || _opcode == Builtins.AVG_POOL || + } else if (_opcode == Builtins.MAX_POOL || _opcode == Builtins.AVG_POOL || _opcode == Builtins.MAX_POOL_BACKWARD || _opcode == Builtins.AVG_POOL_BACKWARD) { HashSet expand = new HashSet<>(); - expand.add("input_shape"); expand.add("pool_size"); expand.add("stride"); expand.add("padding"); + expand.add("input_shape"); + expand.add("pool_size"); + expand.add("stride"); + expand.add("padding"); paramExpression = expandListParams(paramExpression, expand); paramExpression.add(new ParameterExpression("filter_shape1", new IntIdentifier(1, this))); paramExpression.add(new ParameterExpression("filter_shape2", new IntIdentifier(1, this))); paramExpression = replaceListParams(paramExpression, "pool_size", "filter_shape", 3); - if(_opcode == Builtins.MAX_POOL_BACKWARD || _opcode == Builtins.AVG_POOL_BACKWARD) + if (_opcode == Builtins.MAX_POOL_BACKWARD || _opcode == Builtins.AVG_POOL_BACKWARD) paramExpression = orderDnnParams(paramExpression, 2); else paramExpression = orderDnnParams(paramExpression, 1); } - } - catch(LanguageException e) { + } catch (LanguageException e) { throw new RuntimeException(e); } return paramExpression; } - + private boolean isValidNoArgumentFunction() { return getOpCode() == Builtins.TIME - || getOpCode() == Builtins.LIST; + || getOpCode() == Builtins.LIST; } /** @@ -609,1302 +624,1310 @@ private boolean isValidNoArgumentFunction() { * statement */ @Override - public void validateExpression(HashMap ids, HashMap constVars, boolean conditional) - { - for(int i=0; i < _args.length; i++ ) { - if (_args[i] instanceof FunctionCallIdentifier){ + public void validateExpression(HashMap ids, HashMap constVars, + boolean conditional) { + for (int i = 0; i < _args.length; i++) { + if (_args[i] instanceof FunctionCallIdentifier) { raiseValidateError("UDF function call not supported as parameter to built-in function call", false); } _args[i].validateExpression(ids, constVars, conditional); } - + // checkIdentifierParams(); String outputName = getTempName(); DataIdentifier output = new DataIdentifier(outputName); output.setParseInfo(this); - - if (getFirstExpr() == null && !isValidNoArgumentFunction()) { // time has no arguments + + if (getFirstExpr() == null && !isValidNoArgumentFunction()) { // time has no arguments raiseValidateError("Function " + this + " has no arguments.", false); } - Identifier id = (_args.length != 0) ? - getFirstExpr().getOutput() : null; + Identifier id = (_args.length != 0) ? getFirstExpr().getOutput() : null; if (_args.length != 0) output.setProperties(this.getFirstExpr().getOutput()); - output.setNnz(-1); //conservatively, cannot use input nnz! + output.setNnz(-1); // conservatively, cannot use input nnz! setOutput(output); - + switch (getOpCode()) { - case EVAL: - case EVALLIST: - if (_args.length == 0) - raiseValidateError("Function eval should provide at least one argument, i.e., the function name.", false); - checkValueTypeParam(_args[0], ValueType.STRING); - boolean listReturn = (getOpCode()==Builtins.EVALLIST); - output.setDataType(listReturn ? DataType.LIST : DataType.MATRIX); - output.setValueType(listReturn ? ValueType.UNKNOWN : ValueType.FP64); - output.setDimensions(-1, -1); - output.setBlocksize(ConfigurationManager.getBlocksize()); - break; - case COLSUM: - case COLMAX: - case COLMIN: - case COLMEAN: - case COLPROD: - case COLSD: - case COLVAR: - // colSums(X); - checkNumParameters(1); - checkMatrixParam(getFirstExpr()); - output.setDataType(DataType.MATRIX); - output.setDimensions(1, id.getDim2()); - output.setBlocksize (id.getBlocksize()); - output.setValueType(id.getValueType()); - break; - case ROWSUM: - case ROWMAX: - case ROWINDEXMAX: - case ROWMIN: - case ROWINDEXMIN: - case ROWMEAN: - case ROWPROD: - case ROWSD: - case ROWVAR: - //rowSums(X); - checkNumParameters(1); - checkMatrixParam(getFirstExpr()); - output.setDataType(DataType.MATRIX); - output.setDimensions(id.getDim1(), 1); - output.setBlocksize (id.getBlocksize()); - output.setValueType(id.getValueType()); - break; - case SUM: - case PROD: - case TRACE: - case SD: - case VAR: - // sum(X); - checkNumParameters(1); - checkMatrixTensorParam(getFirstExpr()); - output.setDataType(DataType.SCALAR); - output.setDimensions(0, 0); - output.setBlocksize(0); - switch (id.getValueType()) { - case STRING: // TODO think about what we want to get when we sum tensor of strings - case CHARACTER: // TODO here also for Characters. - case FP64: - case FP32: - output.setValueType(ValueType.FP64); - break; - case INT64: - case INT32: - case UINT8: - case UINT4: - case BOOLEAN: - output.setValueType(ValueType.INT64); - break; - case UNKNOWN: - throw new NotImplementedException(); - } - break; - - case MEAN: - //checkNumParameters(2, false); // mean(Y) or mean(Y,W) - if (getSecondExpr() != null) { + case EVAL: + case EVALLIST: + if (_args.length == 0) + raiseValidateError("Function eval should provide at least one argument, i.e., the function name.", + false); + checkValueTypeParam(_args[0], ValueType.STRING); + boolean listReturn = (getOpCode() == Builtins.EVALLIST); + output.setDataType(listReturn ? DataType.LIST : DataType.MATRIX); + output.setValueType(listReturn ? ValueType.UNKNOWN : ValueType.FP64); + output.setDimensions(-1, -1); + output.setBlocksize(ConfigurationManager.getBlocksize()); + break; + case COLSUM: + case COLMAX: + case COLMIN: + case COLMEAN: + case COLPROD: + case COLSD: + case COLVAR: + // colSums(X); + checkNumParameters(1); + checkMatrixParam(getFirstExpr()); + output.setDataType(DataType.MATRIX); + output.setDimensions(1, id.getDim2()); + output.setBlocksize(id.getBlocksize()); + output.setValueType(id.getValueType()); + break; + case ROWSUM: + case ROWMAX: + case ROWINDEXMAX: + case ROWMIN: + case ROWINDEXMIN: + case ROWMEAN: + case ROWPROD: + case ROWSD: + case ROWVAR: + // rowSums(X); + checkNumParameters(1); + checkMatrixParam(getFirstExpr()); + output.setDataType(DataType.MATRIX); + output.setDimensions(id.getDim1(), 1); + output.setBlocksize(id.getBlocksize()); + output.setValueType(id.getValueType()); + break; + case SUM: + case PROD: + case TRACE: + case SD: + case VAR: + // sum(X); + checkNumParameters(1); + checkMatrixTensorParam(getFirstExpr()); + output.setDataType(DataType.SCALAR); + output.setDimensions(0, 0); + output.setBlocksize(0); + switch (id.getValueType()) { + case STRING: // TODO think about what we want to get when we sum tensor of strings + case CHARACTER: // TODO here also for Characters. + case FP64: + case FP32: + output.setValueType(ValueType.FP64); + break; + case INT64: + case INT32: + case UINT8: + case UINT4: + case BOOLEAN: + output.setValueType(ValueType.INT64); + break; + case UNKNOWN: + throw new NotImplementedException(); + } + break; + + case MEAN: + // checkNumParameters(2, false); // mean(Y) or mean(Y,W) + if (getSecondExpr() != null) { + checkNumParameters(2); + } else { + checkNumParameters(1); + } + + checkMatrixParam(getFirstExpr()); + if (getSecondExpr() != null) { + // x = mean(Y,W); + checkMatchingDimensions(getFirstExpr(), getSecondExpr()); + } + + output.setDataType(DataType.SCALAR); + output.setDimensions(0, 0); + output.setBlocksize(0); + output.setValueType(id.getValueType()); + break; + + case XOR: + case BITWAND: + case BITWOR: + case BITWXOR: + case BITWSHIFTL: + case BITWSHIFTR: checkNumParameters(2); - } - else { + setBinaryOutputProperties(output); + break; + + case MIN: + case MAX: + // min(X), min(X,s), min(s,X), min(s,r), min(X,Y) + if (getSecondExpr() == null) { // unary + checkNumParameters(1); + checkMatrixParam(getFirstExpr()); + output.setDataType(DataType.SCALAR); + output.setValueType(id.getValueType()); + output.setDimensions(0, 0); + output.setBlocksize(0); + } else if (getAllExpr().length == 2) { // binary + checkNumParameters(2); + setBinaryOutputProperties(output); + } else { // nary + for (Expression e : getAllExpr()) + checkMatrixScalarParam(e); + setNaryOutputProperties(output); + } + break; + + case CUMSUM: + case CUMPROD: + case CUMSUMPROD: + case CUMMIN: + case CUMMAX: + // cumsum(X); checkNumParameters(1); - } - - checkMatrixParam(getFirstExpr()); - if ( getSecondExpr() != null ) { - // x = mean(Y,W); - checkMatchingDimensions(getFirstExpr(), getSecondExpr()); - } - - output.setDataType(DataType.SCALAR); - output.setDimensions(0, 0); - output.setBlocksize(0); - output.setValueType(id.getValueType()); - break; - - case XOR: - case BITWAND: - case BITWOR: - case BITWXOR: - case BITWSHIFTL: - case BITWSHIFTR: - checkNumParameters(2); - setBinaryOutputProperties(output); - break; - - case MIN: - case MAX: - //min(X), min(X,s), min(s,X), min(s,r), min(X,Y) - if (getSecondExpr() == null) { //unary + checkMatrixParam(getFirstExpr()); + boolean cumSP = getOpCode() == Builtins.CUMSUMPROD; + if (cumSP && id.getDim2() > 2) + raiseValidateError("Cumsumprod only supported over two-column matrices", conditional); + + output.setDataType(DataType.MATRIX); + output.setDimensions(id.getDim1(), cumSP ? 1 : id.getDim2()); + output.setBlocksize(id.getBlocksize()); + output.setValueType(id.getValueType()); + + break; + case CAST_AS_SCALAR: + checkNumParameters(1); + checkDataTypeParam(getFirstExpr(), + DataType.MATRIX, DataType.FRAME, DataType.LIST); + if ((getFirstExpr().getOutput().getDim1() != -1 && getFirstExpr().getOutput().getDim1() != 1) + || (getFirstExpr().getOutput().getDim2() != -1 && getFirstExpr().getOutput().getDim2() != 1)) { + raiseValidateError( + "dimension mismatch while casting matrix to scalar: dim1: " + + getFirstExpr().getOutput().getDim1() + + " dim2 " + getFirstExpr().getOutput().getDim2(), + conditional, LanguageErrorCodes.INVALID_PARAMETERS); + } + output.setDataType(DataType.SCALAR); + output.setDimensions(0, 0); + output.setBlocksize(0); + output.setValueType((id.getValueType() != ValueType.UNKNOWN + || id.getDataType() == DataType.LIST) ? id.getValueType() : ValueType.FP64); + break; + case CAST_AS_MATRIX: checkNumParameters(1); + checkDataTypeParam(getFirstExpr(), + DataType.SCALAR, DataType.FRAME, DataType.LIST); + output.setDataType(DataType.MATRIX); + output.setDimensions(id.getDim1(), id.getDim2()); + if (getFirstExpr().getOutput().getDataType() == DataType.SCALAR) + output.setDimensions(1, 1); // correction scalars + if (getFirstExpr().getOutput().getDataType() == DataType.LIST) + output.setDimensions(-1, -1); // correction list: arbitrary object + output.setBlocksize(id.getBlocksize()); + output.setValueType(ValueType.FP64); // matrices always in double + break; + case CAST_AS_LIST: // list unnesting + checkNumParameters(1); + checkDataTypeParam(getFirstExpr(), DataType.LIST); + output.setDataType(DataType.LIST); + output.setDimensions(-1, 1); + output.setBlocksize(id.getBlocksize()); + output.setValueType(ValueType.UNKNOWN); + break; + case TYPEOF: + case DETECTSCHEMA: + case COLNAMES: + checkNumParameters(1); + checkMatrixFrameParam(getFirstExpr()); + output.setDataType(DataType.FRAME); + output.setDimensions(1, id.getDim2()); + output.setBlocksize(id.getBlocksize()); + output.setValueType(ValueType.STRING); + break; + case CAST_AS_FRAME: + // operation as.frame + // overloaded to take either one argument or 2 where second is column names + if (getSecondExpr() == null) {// there is no column names + checkNumParameters(1); + } else { // there is column names + checkNumParameters(2); + checkDataTypeParam(getSecondExpr(), DataType.LIST); + } + + checkDataTypeParam(getFirstExpr(), DataType.SCALAR, DataType.MATRIX, DataType.LIST); + output.setDataType(DataType.FRAME); + output.setDimensions(id.getDim1(), id.getDim2()); + if (getFirstExpr().getOutput().getDataType() == DataType.SCALAR) + output.setDimensions(1, 1); // correction scalars + if (getFirstExpr().getOutput().getDataType() == DataType.LIST) + output.setDimensions(-1, -1); // correction list: arbitrary object + output.setBlocksize(id.getBlocksize()); + output.setValueType(id.getValueType()); + break; + case CAST_AS_DOUBLE: + checkNumParameters(1); + checkScalarParam(getFirstExpr()); + output.setDataType(DataType.SCALAR); + // output.setDataType(id.getDataType()); //TODO whenever we support multiple + // matrix value types, currently noop. + output.setDimensions(0, 0); + output.setBlocksize(0); + output.setValueType(ValueType.FP64); + break; + case CAST_AS_INT: + checkNumParameters(1); + checkScalarParam(getFirstExpr()); + output.setDataType(DataType.SCALAR); + // output.setDataType(id.getDataType()); //TODO whenever we support multiple + // matrix value types, currently noop. + output.setDimensions(0, 0); + output.setBlocksize(0); + output.setValueType(ValueType.INT64); + break; + case CAST_AS_BOOLEAN: + checkNumParameters(1); + checkScalarParam(getFirstExpr()); + output.setDataType(DataType.SCALAR); + // output.setDataType(id.getDataType()); //TODO whenever we support multiple + // matrix value types, currently noop. + output.setDimensions(0, 0); + output.setBlocksize(0); + output.setValueType(ValueType.BOOLEAN); + break; + + case IFELSE: + checkNumParameters(3); + setTernaryOutputProperties(output, conditional); + break; + + case CBIND: + case RBIND: + // scalar string append (string concatenation with \n) + if (getFirstExpr().getOutput().getDataType() == DataType.SCALAR) { + checkNumParameters(2); + checkScalarParam(getFirstExpr()); + checkScalarParam(getSecondExpr()); + checkValueTypeParam(getFirstExpr(), ValueType.STRING); + checkValueTypeParam(getSecondExpr(), ValueType.STRING); + } + // append (rbind/cbind) all the elements of a list + else if (getAllExpr().length == 1) { + checkDataTypeParam(getFirstExpr(), DataType.LIST); + } else { + if (getAllExpr().length < 2) + raiseValidateError("Invalid number of arguments for " + getOpCode(), conditional); + // list append + if (getFirstExpr().getOutput().getDataType().isList()) + for (int i = 1; i < getAllExpr().length; i++) + checkDataTypeParam(getExpr(i), DataType.SCALAR, DataType.MATRIX, DataType.FRAME, + DataType.LIST); + // matrix append (rbind/cbind) + else + for (int i = 0; i < getAllExpr().length; i++) + checkMatrixFrameParam(getExpr(i)); + } + + output.setDataType(id.getDataType()); + output.setValueType(id.getValueType()); + + // special handling of concatenating all list elements + if (id.getDataType() == DataType.LIST && getAllExpr().length == 1) { + output.setDataType(DataType.MATRIX); + output.setValueType(ValueType.FP64); + } + + // set output dimensions and validate consistency + long m1rlen = getFirstExpr().getOutput().getDim1(); + long m1clen = getFirstExpr().getOutput().getDim2(); + long appendDim1 = m1rlen, appendDim2 = m1clen; + + // best-effort dimension propagation and validation + if (id.getDataType() == DataType.LIST) { + appendDim1 = -1; + appendDim2 = -1; + } else { + for (int i = 1; i < getAllExpr().length; i++) { + long m2rlen = getExpr(i).getOutput().getDim1(); + long m2clen = getExpr(i).getOutput().getDim2(); + + if (getOpCode() == Builtins.CBIND) { + if (m1rlen >= 0 && m2rlen >= 0 && m1rlen != m2rlen) { + raiseValidateError("inputs to cbind must have same number of rows: input 1 rows: " + + m1rlen + ", input 2 rows: " + m2rlen, conditional, + LanguageErrorCodes.INVALID_PARAMETERS); + } + appendDim1 = (m2rlen >= 0) ? m2rlen : appendDim1; + appendDim2 = (appendDim2 >= 0 && m2clen >= 0) ? appendDim2 + m2clen : -1; + } else if (getOpCode() == Builtins.RBIND) { + if (m1clen >= 0 && m2clen >= 0 && m1clen != m2clen) { + raiseValidateError( + "inputs to rbind must have same number of columns: input 1 columns: " + + m1clen + ", input 2 columns: " + m2clen, + conditional, LanguageErrorCodes.INVALID_PARAMETERS); + } + appendDim1 = (appendDim1 >= 0 && m2rlen >= 0) ? appendDim1 + m2rlen : -1; + appendDim2 = (m2clen >= 0) ? m2clen : appendDim2; + } + } + } + + output.setDimensions(appendDim1, appendDim2); + output.setBlocksize(id.getBlocksize()); + + break; + + case PPRED: + // TODO: remove this when ppred has been removed from DML + raiseValidateError("ppred() has been deprecated. Please use the operator directly.", true); + + // ppred (X,Y, "<"); ppred (X,y, "<"); ppred (y,X, "<"); + checkNumParameters(3); + + DataType dt1 = getFirstExpr().getOutput().getDataType(); + DataType dt2 = getSecondExpr().getOutput().getDataType(); + + // check input data types + if (dt1 == DataType.SCALAR && dt2 == DataType.SCALAR) { + raiseValidateError("ppred() requires at least one matrix input.", conditional, + LanguageErrorCodes.INVALID_PARAMETERS); + } + if (dt1 == DataType.MATRIX) + checkMatrixParam(getFirstExpr()); + if (dt2 == DataType.MATRIX) + checkMatrixParam(getSecondExpr()); + + // check operator + if (getThirdExpr().getOutput().getDataType() != DataType.SCALAR || + getThirdExpr().getOutput().getValueType() != ValueType.STRING) { + raiseValidateError("Third argument in ppred() is not an operator ", conditional, + LanguageErrorCodes.INVALID_PARAMETERS); + } + + setBinaryOutputProperties(output); + break; + + case TRANS: + checkNumParameters(1); + checkMatrixParam(getFirstExpr()); + output.setDataType(DataType.MATRIX); + output.setDimensions(id.getDim2(), id.getDim1()); + output.setBlocksize(id.getBlocksize()); + output.setValueType(id.getValueType()); + break; + + case REV: + checkNumParameters(1); + checkMatrixParam(getFirstExpr()); + output.setDataType(DataType.MATRIX); + output.setDimensions(id.getDim1(), id.getDim2()); + output.setBlocksize(id.getBlocksize()); + output.setValueType(id.getValueType()); + break; + + case DIAG: + checkNumParameters(1); + checkMatrixParam(getFirstExpr()); + output.setDataType(DataType.MATRIX); + if (id.getDim2() != -1) { // type known + if (id.getDim2() == 1) { + // diag V2M + output.setDimensions(id.getDim1(), id.getDim1()); + } else { + if (id.getDim1() != id.getDim2()) { + raiseValidateError( + "diag can either: (1) create diagonal matrix from (n x 1) matrix, or (2) take diagonal from a square matrix. " + + "Error invoking diag on matrix with dimensions (" + + id.getDim1() + "," + id.getDim2() + + ") in " + this.toString(), + conditional, LanguageErrorCodes.INVALID_PARAMETERS); + } + // diag M2V + output.setDimensions(id.getDim1(), 1); + } + } + output.setBlocksize(id.getBlocksize()); + output.setValueType(id.getValueType()); + break; + case NROW: + case NCOL: + case LENGTH: + checkNumParameters(1); + checkDataTypeParam(getFirstExpr(), + DataType.FRAME, DataType.LIST, DataType.MATRIX); + output.setDataType(DataType.SCALAR); + output.setDimensions(0, 0); + output.setBlocksize(0); + output.setValueType(ValueType.INT64); + break; + case LINEAGE: + checkNumParameters(1); + checkDataTypeParam(getFirstExpr(), + DataType.MATRIX, DataType.FRAME, DataType.LIST); + output.setDataType(DataType.SCALAR); + output.setDimensions(0, 0); + output.setBlocksize(0); + output.setValueType(ValueType.STRING); + break; + case LIST: + output.setDataType(DataType.LIST); + output.setValueType(ValueType.UNKNOWN); + output.setDimensions(getAllExpr().length, 1); + output.setBlocksize(-1); + break; + case EXISTS: + checkNumParameters(1); + checkStringOrDataIdentifier(getFirstExpr()); + output.setDataType(DataType.SCALAR); + output.setDimensions(0, 0); + output.setBlocksize(0); + output.setValueType(ValueType.BOOLEAN); + break; + + // Contingency tables + case TABLE: + + /* + * Allowed #of arguments: 2,3,4,5,6 + * table(A,B) + * table(A,B,W) + * table(A,B,1) + * table(A,B,dim1,dim2) + * table(A,B,W,dim1,dim2) + * table(A,B,1,dim1,dim2) + * table(A,B,1,dim1,dim2,TRUE) + */ + + // Check for validity of input arguments, and setup output dimensions + + // First input: is always of type MATRIX checkMatrixParam(getFirstExpr()); + + if (getSecondExpr() == null) + raiseValidateError("Invalid number of arguments to table(). " + + "The table() function requires 2, 3, 4, 5, or 6 arguments.", conditional); + + // Second input: can be MATRIX or SCALAR + // cases: table(A,B) or table(A,1) + if (getSecondExpr().getOutput().getDataType() == DataType.MATRIX) + checkMatchingDimensions(getFirstExpr(), getSecondExpr()); + + long outputDim1 = -1, outputDim2 = -1; + + switch (_args.length) { + case 2: + // nothing to do + break; + + case 3: + // case - table w/ weights + // - weights specified as a matrix: table(A,B,W) or table(A,1,W) + // - weights specified as a scalar: table(A,B,1) or table(A,1,1) + if (getThirdExpr().getOutput().getDataType() == DataType.MATRIX) + checkMatchingDimensions(getFirstExpr(), getThirdExpr()); + break; + + case 4: + // case - table w/ output dimensions: table(A,B,dim1,dim2) or + // table(A,1,dim1,dim2) + // third and fourth arguments must be scalars + if (getThirdExpr().getOutput().getDataType() != DataType.SCALAR + || _args[3].getOutput().getDataType() != DataType.SCALAR) { + raiseValidateError( + "Invalid argument types to table(): output dimensions must be of type scalar: " + + this.toString(), + conditional, LanguageErrorCodes.INVALID_PARAMETERS); + } else { + // constant propagation + if (getThirdExpr() instanceof DataIdentifier + && constVars.containsKey(((DataIdentifier) getThirdExpr()).getName()) + && !conditional) + _args[2] = constVars.get(((DataIdentifier) getThirdExpr()).getName()); + if (_args[3] instanceof DataIdentifier + && constVars.containsKey(((DataIdentifier) _args[3]).getName()) && !conditional) + _args[3] = constVars.get(((DataIdentifier) _args[3]).getName()); + + if (getThirdExpr().getOutput() instanceof ConstIdentifier) + outputDim1 = ((ConstIdentifier) getThirdExpr().getOutput()).getLongValue(); + if (_args[3].getOutput() instanceof ConstIdentifier) + outputDim2 = ((ConstIdentifier) _args[3].getOutput()).getLongValue(); + } + break; + + case 5: + case 6: + // case - table w/ weights and output dimensions: + // - table(A,B,W,dim1,dim2) or table(A,1,W,dim1,dim2) + // - table(A,B,1,dim1,dim2) or table(A,1,1,dim1,dim2) + + if (getThirdExpr().getOutput().getDataType() == DataType.MATRIX) + checkMatchingDimensions(getFirstExpr(), getThirdExpr()); + + // fourth and fifth arguments must be scalars + if (_args[3].getOutput().getDataType() != DataType.SCALAR + || _args[4].getOutput().getDataType() != DataType.SCALAR) { + raiseValidateError( + "Invalid argument types to table(): output dimensions must be of type scalar: " + + this.toString(), + conditional, LanguageErrorCodes.INVALID_PARAMETERS); + } else { + // constant propagation + if (_args[3] instanceof DataIdentifier + && constVars.containsKey(((DataIdentifier) _args[3]).getName()) && !conditional) + _args[3] = constVars.get(((DataIdentifier) _args[3]).getName()); + if (_args[4] instanceof DataIdentifier + && constVars.containsKey(((DataIdentifier) _args[4]).getName()) && !conditional) + _args[4] = constVars.get(((DataIdentifier) _args[4]).getName()); + + if (_args[3].getOutput() instanceof ConstIdentifier) + outputDim1 = ((ConstIdentifier) _args[3].getOutput()).getLongValue(); + if (_args[4].getOutput() instanceof ConstIdentifier) + outputDim2 = ((ConstIdentifier) _args[4].getOutput()).getLongValue(); + } + if (_args.length == 6) { + if (!_args[5].getOutput().isScalarBoolean()) + raiseValidateError( + "The 6th ctable parameter (outputEmptyBlocks) must be a boolean literal.", + conditional); + } + break; + + default: + raiseValidateError("Invalid number of arguments to table(): " + + this.toString(), conditional, LanguageErrorCodes.INVALID_PARAMETERS); + } + // The dimensions for the output matrix will be known only at the + // run time + output.setDimensions(outputDim1, outputDim2); + output.setBlocksize(-1); + output.setDataType(DataType.MATRIX); + output.setValueType(ValueType.FP64); + break; + + case MOMENT: + checkMatrixParam(getFirstExpr()); + if (getThirdExpr() != null) { + checkNumParameters(3); + checkMatrixParam(getSecondExpr()); + checkMatchingDimensions(getFirstExpr(), getSecondExpr()); + checkScalarParam(getThirdExpr()); + } else { + checkNumParameters(2); + checkScalarParam(getSecondExpr()); + } + + // output is a scalar output.setDataType(DataType.SCALAR); + output.setValueType(ValueType.FP64); + output.setDimensions(0, 0); + output.setBlocksize(0); + break; + + case COV: + /* + * x = cov(V1,V2) or xw = cov(V1,V2,W) + */ + if (getThirdExpr() != null) { + checkNumParameters(3); + } else { + checkNumParameters(2); + } + checkMatrixParam(getFirstExpr()); + checkMatrixParam(getSecondExpr()); + checkMatchingDimensions(getFirstExpr(), getSecondExpr()); + + if (getThirdExpr() != null) { + checkMatrixParam(getThirdExpr()); + checkMatchingDimensions(getFirstExpr(), getThirdExpr()); + } + + // output is a scalar + output.setDataType(DataType.SCALAR); + output.setValueType(ValueType.FP64); + output.setDimensions(0, 0); + output.setBlocksize(0); + break; + + case QUANTILE: + /* + * q = quantile(V1,0.5) computes median in V1 + * or Q = quantile(V1,P) computes the vector of quantiles as specified by P + * or qw = quantile(V1,W,0.5) computes median when weights (W) are given + * or QW = quantile(V1,W,P) computes the vector of quantiles as specified by P, + * when weights (W) are given + */ + if (getThirdExpr() != null) { + checkNumParameters(3); + } else { + checkNumParameters(2); + } + + // first parameter must always be a 1D matrix + check1DMatrixParam(getFirstExpr()); + + // check for matching dimensions for other matrix parameters + if (getThirdExpr() != null) { + checkMatrixParam(getSecondExpr()); + checkMatchingDimensions(getFirstExpr(), getSecondExpr()); + } + + // set the properties for _output expression + // output dimensions = dimensions of second, if third is null + // = dimensions of the third, otherwise. + + if (getThirdExpr() != null) { + output.setDimensions(getThirdExpr().getOutput().getDim1(), getThirdExpr().getOutput().getDim2()); + output.setBlocksize(getThirdExpr().getOutput().getBlocksize()); + output.setDataType(getThirdExpr().getOutput().getDataType()); + } else { + output.setDimensions(getSecondExpr().getOutput().getDim1(), getSecondExpr().getOutput().getDim2()); + output.setBlocksize(getSecondExpr().getOutput().getBlocksize()); + output.setDataType(getSecondExpr().getOutput().getDataType()); + } + break; + + case INTERQUANTILE: + if (getThirdExpr() != null) { + checkNumParameters(3); + } else { + checkNumParameters(2); + } + checkMatrixParam(getFirstExpr()); + if (getThirdExpr() != null) { + // i.e., second input is weight vector + checkMatrixParam(getSecondExpr()); + checkMatchingDimensionsQuantile(); + } + + if ((getThirdExpr() == null && getSecondExpr().getOutput().getDataType() != DataType.SCALAR) + && (getThirdExpr() != null && getThirdExpr().getOutput().getDataType() != DataType.SCALAR)) { + + raiseValidateError("Invalid parameters to " + this.getOpCode(), conditional, + LanguageErrorCodes.INVALID_PARAMETERS); + } + + output.setValueType(id.getValueType()); + // output dimensions are unknown + output.setDimensions(-1, -1); + output.setBlocksize(-1); + output.setDataType(DataType.MATRIX); + break; + + case IQM: + /* + * Usage: iqm = InterQuartileMean(A,W); iqm = InterQuartileMean(A); + */ + if (getSecondExpr() != null) { + checkNumParameters(2); + } else { + checkNumParameters(1); + } + checkMatrixParam(getFirstExpr()); + + if (getSecondExpr() != null) { + // i.e., second input is weight vector + checkMatrixParam(getSecondExpr()); + checkMatchingDimensions(getFirstExpr(), getSecondExpr()); + } + + // Output is a scalar output.setValueType(id.getValueType()); output.setDimensions(0, 0); output.setBlocksize(0); - } - else if( getAllExpr().length == 2 ) { //binary - checkNumParameters(2); - setBinaryOutputProperties(output); - } - else { //nary - for( Expression e : getAllExpr() ) - checkMatrixScalarParam(e); - setNaryOutputProperties(output); - } - break; - - case CUMSUM: - case CUMPROD: - case CUMSUMPROD: - case CUMMIN: - case CUMMAX: - // cumsum(X); - checkNumParameters(1); - checkMatrixParam(getFirstExpr()); - boolean cumSP = getOpCode() == Builtins.CUMSUMPROD; - if( cumSP && id.getDim2() > 2 ) - raiseValidateError("Cumsumprod only supported over two-column matrices", conditional); - - output.setDataType(DataType.MATRIX); - output.setDimensions(id.getDim1(), cumSP ? 1 : id.getDim2()); - output.setBlocksize (id.getBlocksize()); - output.setValueType(id.getValueType()); - - break; - case CAST_AS_SCALAR: - checkNumParameters(1); - checkDataTypeParam(getFirstExpr(), - DataType.MATRIX, DataType.FRAME, DataType.LIST); - if (( getFirstExpr().getOutput().getDim1() != -1 && getFirstExpr().getOutput().getDim1() !=1) - || ( getFirstExpr().getOutput().getDim2() != -1 && getFirstExpr().getOutput().getDim2() !=1)) { - raiseValidateError("dimension mismatch while casting matrix to scalar: dim1: " + getFirstExpr().getOutput().getDim1() - + " dim2 " + getFirstExpr().getOutput().getDim2(), conditional, LanguageErrorCodes.INVALID_PARAMETERS); - } - output.setDataType(DataType.SCALAR); - output.setDimensions(0, 0); - output.setBlocksize(0); - output.setValueType((id.getValueType()!=ValueType.UNKNOWN - || id.getDataType()==DataType.LIST) ? id.getValueType() : ValueType.FP64); - break; - case CAST_AS_MATRIX: - checkNumParameters(1); - checkDataTypeParam(getFirstExpr(), - DataType.SCALAR, DataType.FRAME, DataType.LIST); - output.setDataType(DataType.MATRIX); - output.setDimensions(id.getDim1(), id.getDim2()); - if( getFirstExpr().getOutput().getDataType()==DataType.SCALAR ) - output.setDimensions(1, 1); //correction scalars - if( getFirstExpr().getOutput().getDataType()==DataType.LIST ) - output.setDimensions(-1, -1); //correction list: arbitrary object - output.setBlocksize(id.getBlocksize()); - output.setValueType(ValueType.FP64); //matrices always in double - break; - case CAST_AS_LIST: //list unnesting - checkNumParameters(1); - checkDataTypeParam(getFirstExpr(), DataType.LIST); - output.setDataType(DataType.LIST); - output.setDimensions(-1, 1); - output.setBlocksize(id.getBlocksize()); - output.setValueType(ValueType.UNKNOWN); - break; - case TYPEOF: - case DETECTSCHEMA: - case COLNAMES: - checkNumParameters(1); - checkMatrixFrameParam(getFirstExpr()); - output.setDataType(DataType.FRAME); - output.setDimensions(1, id.getDim2()); - output.setBlocksize (id.getBlocksize()); - output.setValueType(ValueType.STRING); - break; - case CAST_AS_FRAME: - // operation as.frame - // overloaded to take either one argument or 2 where second is column names - if( getSecondExpr() == null) {// there is no column names + output.setDataType(DataType.SCALAR); + + break; + + case ISNA: + case ISNAN: + case ISINF: checkNumParameters(1); - } - else{ // there is column names - checkNumParameters(2); - checkDataTypeParam(getSecondExpr(), DataType.LIST); - } + checkMatrixScalarParam(getFirstExpr()); + output.setDataType(id.getDataType()); + output.setDimensions(id.getDim1(), id.getDim2()); + output.setBlocksize(id.getBlocksize()); + // TODO set output type to boolean when supported + output.setValueType(id.getValueType()); + break; - checkDataTypeParam(getFirstExpr(), DataType.SCALAR, DataType.MATRIX, DataType.LIST); - output.setDataType(DataType.FRAME); - output.setDimensions(id.getDim1(), id.getDim2()); - if(getFirstExpr().getOutput().getDataType() == DataType.SCALAR) - output.setDimensions(1, 1); // correction scalars - if(getFirstExpr().getOutput().getDataType() == DataType.LIST) - output.setDimensions(-1, -1); // correction list: arbitrary object - output.setBlocksize(id.getBlocksize()); - output.setValueType(id.getValueType()); - break; - case CAST_AS_DOUBLE: - checkNumParameters(1); - checkScalarParam(getFirstExpr()); - output.setDataType(DataType.SCALAR); - //output.setDataType(id.getDataType()); //TODO whenever we support multiple matrix value types, currently noop. - output.setDimensions(0, 0); - output.setBlocksize(0); - output.setValueType(ValueType.FP64); - break; - case CAST_AS_INT: - checkNumParameters(1); - checkScalarParam(getFirstExpr()); - output.setDataType(DataType.SCALAR); - //output.setDataType(id.getDataType()); //TODO whenever we support multiple matrix value types, currently noop. - output.setDimensions(0, 0); - output.setBlocksize(0); - output.setValueType(ValueType.INT64); - break; - case CAST_AS_BOOLEAN: - checkNumParameters(1); - checkScalarParam(getFirstExpr()); - output.setDataType(DataType.SCALAR); - //output.setDataType(id.getDataType()); //TODO whenever we support multiple matrix value types, currently noop. - output.setDimensions(0, 0); - output.setBlocksize(0); - output.setValueType(ValueType.BOOLEAN); - break; - - case IFELSE: - checkNumParameters(3); - setTernaryOutputProperties(output, conditional); - break; - - case CBIND: - case RBIND: - //scalar string append (string concatenation with \n) - if( getFirstExpr().getOutput().getDataType()==DataType.SCALAR ) { - checkNumParameters(2); - checkScalarParam(getFirstExpr()); - checkScalarParam(getSecondExpr()); - checkValueTypeParam(getFirstExpr(), ValueType.STRING); - checkValueTypeParam(getSecondExpr(), ValueType.STRING); - } - // append (rbind/cbind) all the elements of a list - else if( getAllExpr().length == 1 ) { - checkDataTypeParam(getFirstExpr(), DataType.LIST); - } - else { - if( getAllExpr().length < 2 ) - raiseValidateError("Invalid number of arguments for "+getOpCode(), conditional); - //list append - if(getFirstExpr().getOutput().getDataType().isList() ) - for(int i=1; i= 0 && m2rlen >= 0 && m1rlen!=m2rlen) { - raiseValidateError("inputs to cbind must have same number of rows: input 1 rows: " + - m1rlen+", input 2 rows: "+m2rlen, conditional, LanguageErrorCodes.INVALID_PARAMETERS); - } - appendDim1 = (m2rlen>=0) ? m2rlen : appendDim1; - appendDim2 = (appendDim2>=0 && m2clen>=0) ? appendDim2 + m2clen : -1; - } - else if( getOpCode() == Builtins.RBIND ) { - if (m1clen >= 0 && m2clen >= 0 && m1clen!=m2clen) { - raiseValidateError("inputs to rbind must have same number of columns: input 1 columns: " + - m1clen+", input 2 columns: "+m2clen, conditional, LanguageErrorCodes.INVALID_PARAMETERS); - } - appendDim1 = (appendDim1>=0 && m2rlen>=0)? appendDim1 + m2rlen : -1; - appendDim2 = (m2clen>=0) ? m2clen : appendDim2; - } - } - } - - output.setDimensions(appendDim1, appendDim2); - output.setBlocksize (id.getBlocksize()); - - break; - - case PPRED: - // TODO: remove this when ppred has been removed from DML - raiseValidateError("ppred() has been deprecated. Please use the operator directly.", true); - - // ppred (X,Y, "<"); ppred (X,y, "<"); ppred (y,X, "<"); - checkNumParameters(3); - - DataType dt1 = getFirstExpr().getOutput().getDataType(); - DataType dt2 = getSecondExpr().getOutput().getDataType(); - - //check input data types - if( dt1 == DataType.SCALAR && dt2 == DataType.SCALAR ) { - raiseValidateError("ppred() requires at least one matrix input.", conditional, LanguageErrorCodes.INVALID_PARAMETERS); - } - if( dt1 == DataType.MATRIX ) + case MEDIAN: + checkNumParameters((getSecondExpr() != null) ? 2 : 1); checkMatrixParam(getFirstExpr()); - if( dt2 == DataType.MATRIX ) - checkMatrixParam(getSecondExpr()); - - //check operator - if (getThirdExpr().getOutput().getDataType() != DataType.SCALAR || - getThirdExpr().getOutput().getValueType() != ValueType.STRING) - { - raiseValidateError("Third argument in ppred() is not an operator ", conditional, LanguageErrorCodes.INVALID_PARAMETERS); - } - - setBinaryOutputProperties(output); - break; - - case TRANS: - checkNumParameters(1); - checkMatrixParam(getFirstExpr()); - output.setDataType(DataType.MATRIX); - output.setDimensions(id.getDim2(), id.getDim1()); - output.setBlocksize (id.getBlocksize()); - output.setValueType(id.getValueType()); - break; - - case REV: - checkNumParameters(1); - checkMatrixParam(getFirstExpr()); - output.setDataType(DataType.MATRIX); - output.setDimensions(id.getDim1(), id.getDim2()); - output.setBlocksize (id.getBlocksize()); - output.setValueType(id.getValueType()); - break; - - case DIAG: - checkNumParameters(1); - checkMatrixParam(getFirstExpr()); - output.setDataType(DataType.MATRIX); - if( id.getDim2() != -1 ) { //type known - if ( id.getDim2() == 1 ) - { - //diag V2M - output.setDimensions(id.getDim1(), id.getDim1()); - } - else - { - if (id.getDim1() != id.getDim2()) { - raiseValidateError("diag can either: (1) create diagonal matrix from (n x 1) matrix, or (2) take diagonal from a square matrix. " - + "Error invoking diag on matrix with dimensions (" - + id.getDim1() + "," + id.getDim2() - + ") in " + this.toString(), conditional, LanguageErrorCodes.INVALID_PARAMETERS); - } - //diag M2V - output.setDimensions(id.getDim1(), 1); - } - } - output.setBlocksize(id.getBlocksize()); - output.setValueType(id.getValueType()); - break; - case NROW: - case NCOL: - case LENGTH: - checkNumParameters(1); - checkDataTypeParam(getFirstExpr(), - DataType.FRAME, DataType.LIST, DataType.MATRIX); - output.setDataType(DataType.SCALAR); - output.setDimensions(0, 0); - output.setBlocksize(0); - output.setValueType(ValueType.INT64); - break; - case LINEAGE: - checkNumParameters(1); - checkDataTypeParam(getFirstExpr(), - DataType.MATRIX, DataType.FRAME, DataType.LIST); - output.setDataType(DataType.SCALAR); - output.setDimensions(0, 0); - output.setBlocksize(0); - output.setValueType(ValueType.STRING); - break; - case LIST: - output.setDataType(DataType.LIST); - output.setValueType(ValueType.UNKNOWN); - output.setDimensions(getAllExpr().length, 1); - output.setBlocksize(-1); - break; - case EXISTS: - checkNumParameters(1); - checkStringOrDataIdentifier(getFirstExpr()); - output.setDataType(DataType.SCALAR); - output.setDimensions(0, 0); - output.setBlocksize(0); - output.setValueType(ValueType.BOOLEAN); - break; - - // Contingency tables - case TABLE: - - /* - * Allowed #of arguments: 2,3,4,5,6 - * table(A,B) - * table(A,B,W) - * table(A,B,1) - * table(A,B,dim1,dim2) - * table(A,B,W,dim1,dim2) - * table(A,B,1,dim1,dim2) - * table(A,B,1,dim1,dim2,TRUE) - */ - - // Check for validity of input arguments, and setup output dimensions - - // First input: is always of type MATRIX - checkMatrixParam(getFirstExpr()); - - if (getSecondExpr() == null) - raiseValidateError("Invalid number of arguments to table(). " - + "The table() function requires 2, 3, 4, 5, or 6 arguments.", conditional); - - // Second input: can be MATRIX or SCALAR - // cases: table(A,B) or table(A,1) - if ( getSecondExpr().getOutput().getDataType() == DataType.MATRIX) - checkMatchingDimensions(getFirstExpr(),getSecondExpr()); - - long outputDim1=-1, outputDim2=-1; - - switch(_args.length) { - case 2: - // nothing to do - break; - - case 3: - // case - table w/ weights - // - weights specified as a matrix: table(A,B,W) or table(A,1,W) - // - weights specified as a scalar: table(A,B,1) or table(A,1,1) - if ( getThirdExpr().getOutput().getDataType() == DataType.MATRIX) - checkMatchingDimensions(getFirstExpr(),getThirdExpr()); - break; - - case 4: - // case - table w/ output dimensions: table(A,B,dim1,dim2) or table(A,1,dim1,dim2) - // third and fourth arguments must be scalars - if ( getThirdExpr().getOutput().getDataType() != DataType.SCALAR || _args[3].getOutput().getDataType() != DataType.SCALAR ) { - raiseValidateError("Invalid argument types to table(): output dimensions must be of type scalar: " - + this.toString(), conditional, LanguageErrorCodes.INVALID_PARAMETERS); - } - else { - // constant propagation - if( getThirdExpr() instanceof DataIdentifier && constVars.containsKey(((DataIdentifier)getThirdExpr()).getName()) && !conditional ) - _args[2] = constVars.get(((DataIdentifier)getThirdExpr()).getName()); - if( _args[3] instanceof DataIdentifier && constVars.containsKey(((DataIdentifier)_args[3]).getName()) && !conditional ) - _args[3] = constVars.get(((DataIdentifier)_args[3]).getName()); - - if ( getThirdExpr().getOutput() instanceof ConstIdentifier ) - outputDim1 = ((ConstIdentifier) getThirdExpr().getOutput()).getLongValue(); - if ( _args[3].getOutput() instanceof ConstIdentifier ) - outputDim2 = ((ConstIdentifier) _args[3].getOutput()).getLongValue(); + + if (getSecondExpr() != null) { + // i.e., second input is weight vector + checkMatrixParam(getSecondExpr()); + checkMatchingDimensions(getFirstExpr(), getSecondExpr()); } + + // Output is a scalar + output.setValueType(id.getValueType()); + output.setDimensions(0, 0); + output.setBlocksize(0); + output.setDataType(DataType.SCALAR); + break; - - case 5: - case 6: - // case - table w/ weights and output dimensions: - // - table(A,B,W,dim1,dim2) or table(A,1,W,dim1,dim2) - // - table(A,B,1,dim1,dim2) or table(A,1,1,dim1,dim2) - - if ( getThirdExpr().getOutput().getDataType() == DataType.MATRIX) - checkMatchingDimensions(getFirstExpr(),getThirdExpr()); - - // fourth and fifth arguments must be scalars - if ( _args[3].getOutput().getDataType() != DataType.SCALAR || _args[4].getOutput().getDataType() != DataType.SCALAR ) { - raiseValidateError("Invalid argument types to table(): output dimensions must be of type scalar: " - + this.toString(), conditional, LanguageErrorCodes.INVALID_PARAMETERS); - } - else { - // constant propagation - if( _args[3] instanceof DataIdentifier && constVars.containsKey(((DataIdentifier)_args[3]).getName()) && !conditional ) - _args[3] = constVars.get(((DataIdentifier)_args[3]).getName()); - if( _args[4] instanceof DataIdentifier && constVars.containsKey(((DataIdentifier)_args[4]).getName()) && !conditional ) - _args[4] = constVars.get(((DataIdentifier)_args[4]).getName()); - - if ( _args[3].getOutput() instanceof ConstIdentifier ) - outputDim1 = ((ConstIdentifier) _args[3].getOutput()).getLongValue(); - if ( _args[4].getOutput() instanceof ConstIdentifier ) - outputDim2 = ((ConstIdentifier) _args[4].getOutput()).getLongValue(); + + case SAMPLE: { + Expression[] in = getAllExpr(); + + for (Expression e : in) + checkScalarParam(e); + + if (in[0].getOutput().getValueType() != ValueType.FP64 + && in[0].getOutput().getValueType() != ValueType.INT64) + throw new LanguageException("First argument to sample() must be a number."); + if (in[1].getOutput().getValueType() != ValueType.FP64 + && in[1].getOutput().getValueType() != ValueType.INT64) + throw new LanguageException("Second argument to sample() must be a number."); + + boolean check = false; + if (isConstant(in[0]) && isConstant(in[1])) { + long range = ((ConstIdentifier) in[0]).getLongValue(); + long size = ((ConstIdentifier) in[1]).getLongValue(); + if (range < size) + check = true; } - if( _args.length == 6 ) { - if( !_args[5].getOutput().isScalarBoolean() ) - raiseValidateError("The 6th ctable parameter (outputEmptyBlocks) must be a boolean literal.", conditional); + + if (in.length == 4) { + checkNumParameters(4); + if (in[3].getOutput().getValueType() != ValueType.INT64) + throw new LanguageException("Fourth argument, seed, to sample() must be an integer value."); + if (in[2].getOutput().getValueType() != ValueType.BOOLEAN) + throw new LanguageException( + "Third argument to sample() must either denote replacement policy (boolean) or seed (integer)."); + } else if (in.length == 3) { + checkNumParameters(3); + if (in[2].getOutput().getValueType() != ValueType.BOOLEAN + && in[2].getOutput().getValueType() != ValueType.INT64) + throw new LanguageException( + "Third argument to sample() must either denote replacement policy (boolean) or seed (integer)."); } - break; - default: - raiseValidateError("Invalid number of arguments to table(): " - + this.toString(), conditional, LanguageErrorCodes.INVALID_PARAMETERS); - } - // The dimensions for the output matrix will be known only at the - // run time - output.setDimensions(outputDim1, outputDim2); - output.setBlocksize (-1); - output.setDataType(DataType.MATRIX); - output.setValueType(ValueType.FP64); - break; - - case MOMENT: - checkMatrixParam(getFirstExpr()); - if (getThirdExpr() != null) { - checkNumParameters(3); - checkMatrixParam(getSecondExpr()); - checkMatchingDimensions(getFirstExpr(),getSecondExpr()); - checkScalarParam(getThirdExpr()); + if (check && in.length >= 3 + && isConstant(in[2]) + && in[2].getOutput().getValueType() == ValueType.BOOLEAN + && !((BooleanIdentifier) in[2]).getValue()) + throw new LanguageException("Sample (size=" + ((ConstIdentifier) in[0]).getLongValue() + + ") larger than population (size=" + ((ConstIdentifier) in[1]).getLongValue() + + ") can only be generated with replacement."); + + // Output is a column vector + output.setDataType(DataType.MATRIX); + output.setValueType(ValueType.FP64); + + if (isConstant(in[1])) + output.setDimensions(((ConstIdentifier) in[1]).getLongValue(), 1); + else + output.setDimensions(-1, 1); + setBlocksize(id.getBlocksize()); + + break; } - else { - checkNumParameters(2); + case SEQ: + + // basic parameter validation + checkScalarParam(getFirstExpr()); checkScalarParam(getSecondExpr()); - } + if (getThirdExpr() != null) { + checkNumParameters(3); + checkScalarParam(getThirdExpr()); + } else + checkNumParameters(2); + + // constant propagation (from, to, incr) + if (!conditional) { + if (getFirstExpr() instanceof DataIdentifier + && constVars.containsKey(((DataIdentifier) getFirstExpr()).getName())) + _args[0] = constVars.get(((DataIdentifier) getFirstExpr()).getName()); + if (getSecondExpr() instanceof DataIdentifier + && constVars.containsKey(((DataIdentifier) getSecondExpr()).getName())) + _args[1] = constVars.get(((DataIdentifier) getSecondExpr()).getName()); + if (getThirdExpr() != null && getThirdExpr() instanceof DataIdentifier + && constVars.containsKey(((DataIdentifier) getThirdExpr()).getName())) + _args[2] = constVars.get(((DataIdentifier) getThirdExpr()).getName()); + } - // output is a scalar - output.setDataType(DataType.SCALAR); - output.setValueType(ValueType.FP64); - output.setDimensions(0, 0); - output.setBlocksize(0); - break; - - case COV: - /* - * x = cov(V1,V2) or xw = cov(V1,V2,W) - */ - if (getThirdExpr() != null) { - checkNumParameters(3); - } - else { - checkNumParameters(2); - } - checkMatrixParam(getFirstExpr()); - checkMatrixParam(getSecondExpr()); - checkMatchingDimensions(getFirstExpr(),getSecondExpr()); - - if (getThirdExpr() != null) { - checkMatrixParam(getThirdExpr()); - checkMatchingDimensions(getFirstExpr(), getThirdExpr()); - } + // check if dimensions can be inferred + long dim1 = -1, dim2 = 1; + if (isConstant(getFirstExpr()) && isConstant(getSecondExpr()) + && (getThirdExpr() != null ? isConstant(getThirdExpr()) : true)) { + double from, to, incr; + try { + from = getDoubleValue(getFirstExpr()); + to = getDoubleValue(getSecondExpr()); + + // Setup the value of increment + // default value: 1 if from <= to; -1 if from > to + if (getThirdExpr() == null) { + expandArguments(); + _args[2] = new DoubleIdentifier(((from > to) ? -1.0 : 1.0), this); + } + incr = getDoubleValue(getThirdExpr()); - // output is a scalar - output.setDataType(DataType.SCALAR); - output.setValueType(ValueType.FP64); - output.setDimensions(0, 0); - output.setBlocksize(0); - break; - - case QUANTILE: - /* - * q = quantile(V1,0.5) computes median in V1 - * or Q = quantile(V1,P) computes the vector of quantiles as specified by P - * or qw = quantile(V1,W,0.5) computes median when weights (W) are given - * or QW = quantile(V1,W,P) computes the vector of quantiles as specified by P, when weights (W) are given - */ - if(getThirdExpr() != null) { - checkNumParameters(3); - } - else { - checkNumParameters(2); - } - - // first parameter must always be a 1D matrix - check1DMatrixParam(getFirstExpr()); - - // check for matching dimensions for other matrix parameters - if (getThirdExpr() != null) { - checkMatrixParam(getSecondExpr()); - checkMatchingDimensions(getFirstExpr(), getSecondExpr()); - } - - // set the properties for _output expression - // output dimensions = dimensions of second, if third is null - // = dimensions of the third, otherwise. - - if (getThirdExpr() != null) { - output.setDimensions(getThirdExpr().getOutput().getDim1(), getThirdExpr().getOutput().getDim2()); - output.setBlocksize(getThirdExpr().getOutput().getBlocksize()); - output.setDataType(getThirdExpr().getOutput().getDataType()); - } else { - output.setDimensions(getSecondExpr().getOutput().getDim1(), getSecondExpr().getOutput().getDim2()); - output.setBlocksize(getSecondExpr().getOutput().getBlocksize()); - output.setDataType(getSecondExpr().getOutput().getDataType()); - } - break; + } catch (LanguageException e) { + throw new LanguageException("Arguments for seq() must be numeric."); + } - case INTERQUANTILE: - if (getThirdExpr() != null) { - checkNumParameters(3); - } - else { + if ((from > to) && (incr >= 0)) + throw new LanguageException("Wrong sign for the increment in a call to seq()"); + + // Both end points of the range must included i.e., [from,to] both inclusive. + // Note that, "to" is included only if (to-from) is perfectly divisible by incr + // For example, seq(0,1,0.5) produces (0.0 0.5 1.0) whereas seq(0,1,0.6) + // produces only (0.0 0.6) but not (0.0 0.6 1.0) + dim1 = UtilFunctions.getSeqLength(from, to, incr); + } + output.setDataType(DataType.MATRIX); + output.setValueType(ValueType.FP64); + output.setDimensions(dim1, dim2); + output.setBlocksize(0); + break; + + case SOLVE: checkNumParameters(2); - } - checkMatrixParam(getFirstExpr()); - if (getThirdExpr() != null) { - // i.e., second input is weight vector + checkMatrixParam(getFirstExpr()); checkMatrixParam(getSecondExpr()); - checkMatchingDimensionsQuantile(); - } - if ((getThirdExpr() == null && getSecondExpr().getOutput().getDataType() != DataType.SCALAR) - && (getThirdExpr() != null && getThirdExpr().getOutput().getDataType() != DataType.SCALAR)) { - - raiseValidateError("Invalid parameters to "+ this.getOpCode(), conditional, LanguageErrorCodes.INVALID_PARAMETERS); - } + if (getSecondExpr().getOutput().dimsKnown() && !is1DMatrix(getSecondExpr())) + raiseValidateError("Second input to solve() must be a vector", conditional); + + if (getFirstExpr().getOutput().dimsKnown() && getSecondExpr().getOutput().dimsKnown() && + getFirstExpr().getOutput().getDim1() != getSecondExpr().getOutput().getDim1() && + getFirstExpr().getOutput().getDim1() != getFirstExpr().getOutput().getDim2()) + raiseValidateError("Dimension mismatch in a call to solve()", conditional); + + output.setDataType(DataType.MATRIX); + output.setValueType(ValueType.FP64); + output.setDimensions(getFirstExpr().getOutput().getDim2(), 1); + output.setBlocksize(0); + break; - output.setValueType(id.getValueType()); - // output dimensions are unknown - output.setDimensions(-1, -1); - output.setBlocksize(-1); - output.setDataType(DataType.MATRIX); - break; - - case IQM: - /* - * Usage: iqm = InterQuartileMean(A,W); iqm = InterQuartileMean(A); - */ - if (getSecondExpr() != null){ - checkNumParameters(2); - } - else { + case INVERSE: checkNumParameters(1); - } - checkMatrixParam(getFirstExpr()); + checkMatrixParam(getFirstExpr()); - if (getSecondExpr() != null) { - // i.e., second input is weight vector - checkMatrixParam(getSecondExpr()); - checkMatchingDimensions(getFirstExpr(), getSecondExpr()); - } + output.setDataType(DataType.MATRIX); + output.setValueType(ValueType.FP64); - // Output is a scalar - output.setValueType(id.getValueType()); - output.setDimensions(0, 0); - output.setBlocksize(0); - output.setDataType(DataType.SCALAR); - - break; - - case ISNA: - case ISNAN: - case ISINF: - checkNumParameters(1); - checkMatrixScalarParam(getFirstExpr()); - output.setDataType(id.getDataType()); - output.setDimensions(id.getDim1(), id.getDim2()); - output.setBlocksize (id.getBlocksize()); - //TODO set output type to boolean when supported - output.setValueType(id.getValueType()); - break; - - case MEDIAN: - checkNumParameters((getSecondExpr()!=null) ? 2 : 1); - checkMatrixParam(getFirstExpr()); - - if (getSecondExpr() != null) { - // i.e., second input is weight vector - checkMatrixParam(getSecondExpr()); - checkMatchingDimensions(getFirstExpr(), getSecondExpr()); - } + Identifier in = getFirstExpr().getOutput(); + if (in.dimsKnown() && in.getDim1() != in.getDim2()) + raiseValidateError("Input to inv() must be square matrix -- given: a " + in.getDim1() + "x" + + in.getDim2() + " matrix.", conditional); - // Output is a scalar - output.setValueType(id.getValueType()); - output.setDimensions(0, 0); - output.setBlocksize(0); - output.setDataType(DataType.SCALAR); - - break; - - case SAMPLE: - { - Expression[] in = getAllExpr(); - - for(Expression e : in) - checkScalarParam(e); - - if (in[0].getOutput().getValueType() != ValueType.FP64 && in[0].getOutput().getValueType() != ValueType.INT64) - throw new LanguageException("First argument to sample() must be a number."); - if (in[1].getOutput().getValueType() != ValueType.FP64 && in[1].getOutput().getValueType() != ValueType.INT64) - throw new LanguageException("Second argument to sample() must be a number."); - - boolean check = false; - if ( isConstant(in[0]) && isConstant(in[1]) ) - { - long range = ((ConstIdentifier)in[0]).getLongValue(); - long size = ((ConstIdentifier)in[1]).getLongValue(); - if ( range < size ) - check = true; - } - - if(in.length == 4 ) - { - checkNumParameters(4); - if (in[3].getOutput().getValueType() != ValueType.INT64) - throw new LanguageException("Fourth argument, seed, to sample() must be an integer value."); - if (in[2].getOutput().getValueType() != ValueType.BOOLEAN ) - throw new LanguageException("Third argument to sample() must either denote replacement policy (boolean) or seed (integer)."); - } - else if(in.length == 3) - { - checkNumParameters(3); - if (in[2].getOutput().getValueType() != ValueType.BOOLEAN - && in[2].getOutput().getValueType() != ValueType.INT64 ) - throw new LanguageException("Third argument to sample() must either denote replacement policy (boolean) or seed (integer)."); + output.setDimensions(in.getDim1(), in.getDim2()); + output.setBlocksize(in.getBlocksize()); + break; + + case CHOLESKY: { + // A = L%*%t(L) where L is the lower triangular matrix + checkNumParameters(1); + checkMatrixParam(getFirstExpr()); + + output.setDataType(DataType.MATRIX); + output.setValueType(ValueType.FP64); + + Identifier inA = getFirstExpr().getOutput(); + if (inA.dimsKnown() && inA.getDim1() != inA.getDim2()) + raiseValidateError("Input to cholesky() must be square matrix -- given: a " + inA.getDim1() + "x" + + inA.getDim2() + " matrix.", conditional); + + output.setDimensions(inA.getDim1(), inA.getDim2()); + output.setBlocksize(inA.getBlocksize()); + break; } - - if ( check && in.length >= 3 - && isConstant(in[2]) - && in[2].getOutput().getValueType() == ValueType.BOOLEAN - && !((BooleanIdentifier)in[2]).getValue() ) - throw new LanguageException("Sample (size=" + ((ConstIdentifier)in[0]).getLongValue() - + ") larger than population (size=" + ((ConstIdentifier)in[1]).getLongValue() - + ") can only be generated with replacement."); - - // Output is a column vector - output.setDataType(DataType.MATRIX); - output.setValueType(ValueType.FP64); - - if ( isConstant(in[1]) ) - output.setDimensions(((ConstIdentifier)in[1]).getLongValue(), 1); - else - output.setDimensions(-1, 1); - setBlocksize(id.getBlocksize()); - - break; - } - case SEQ: - - //basic parameter validation - checkScalarParam(getFirstExpr()); - checkScalarParam(getSecondExpr()); - if ( getThirdExpr() != null ) { + + case OUTER: + Identifier id2 = this.getSecondExpr().getOutput(); + + // check input types and characteristics checkNumParameters(3); + checkMatrixParam(getFirstExpr()); + checkMatrixParam(getSecondExpr()); checkScalarParam(getThirdExpr()); - } - else - checkNumParameters(2); - - // constant propagation (from, to, incr) - if( !conditional ) { - if( getFirstExpr() instanceof DataIdentifier && constVars.containsKey(((DataIdentifier)getFirstExpr()).getName()) ) - _args[0] = constVars.get(((DataIdentifier)getFirstExpr()).getName()); - if( getSecondExpr() instanceof DataIdentifier && constVars.containsKey(((DataIdentifier)getSecondExpr()).getName()) ) - _args[1] = constVars.get(((DataIdentifier)getSecondExpr()).getName()); - if( getThirdExpr()!=null && getThirdExpr() instanceof DataIdentifier && constVars.containsKey(((DataIdentifier)getThirdExpr()).getName()) ) - _args[2] = constVars.get(((DataIdentifier)getThirdExpr()).getName()); - } - - // check if dimensions can be inferred - long dim1=-1, dim2=1; - if ( isConstant(getFirstExpr()) && isConstant(getSecondExpr()) && (getThirdExpr() != null ? isConstant(getThirdExpr()) : true) ) { - double from, to, incr; - try { - from = getDoubleValue(getFirstExpr()); - to = getDoubleValue(getSecondExpr()); - - // Setup the value of increment - // default value: 1 if from <= to; -1 if from > to - if(getThirdExpr() == null) { - expandArguments(); - _args[2] = new DoubleIdentifier(((from > to) ? -1.0 : 1.0), this); - } - incr = getDoubleValue(getThirdExpr()); - - } - catch (LanguageException e) { - throw new LanguageException("Arguments for seq() must be numeric."); + checkValueTypeParam(getThirdExpr(), ValueType.STRING); + if (id.getDim2() > 1 || id2.getDim1() > 1) { + raiseValidateError("Outer vector operations require a common dimension of one: " + + id.getDim1() + "x" + id.getDim2() + " o " + id2.getDim1() + "x" + id2.getDim2() + ".", + false); } - if( (from > to) && (incr >= 0) ) - throw new LanguageException("Wrong sign for the increment in a call to seq()"); - - // Both end points of the range must included i.e., [from,to] both inclusive. - // Note that, "to" is included only if (to-from) is perfectly divisible by incr - // For example, seq(0,1,0.5) produces (0.0 0.5 1.0) whereas seq(0,1,0.6) produces only (0.0 0.6) but not (0.0 0.6 1.0) - dim1 = UtilFunctions.getSeqLength(from, to, incr); - } - output.setDataType(DataType.MATRIX); - output.setValueType(ValueType.FP64); - output.setDimensions(dim1, dim2); - output.setBlocksize(0); - break; - - case SOLVE: - checkNumParameters(2); - checkMatrixParam(getFirstExpr()); - checkMatrixParam(getSecondExpr()); - - if ( getSecondExpr().getOutput().dimsKnown() && !is1DMatrix(getSecondExpr()) ) - raiseValidateError("Second input to solve() must be a vector", conditional); - - if ( getFirstExpr().getOutput().dimsKnown() && getSecondExpr().getOutput().dimsKnown() && - getFirstExpr().getOutput().getDim1() != getSecondExpr().getOutput().getDim1() && - getFirstExpr().getOutput().getDim1() != getFirstExpr().getOutput().getDim2()) - raiseValidateError("Dimension mismatch in a call to solve()", conditional); - - output.setDataType(DataType.MATRIX); - output.setValueType(ValueType.FP64); - output.setDimensions(getFirstExpr().getOutput().getDim2(), 1); - output.setBlocksize(0); - break; - - case INVERSE: - checkNumParameters(1); - checkMatrixParam(getFirstExpr()); - - output.setDataType(DataType.MATRIX); - output.setValueType(ValueType.FP64); - - Identifier in = getFirstExpr().getOutput(); - if(in.dimsKnown() && in.getDim1() != in.getDim2()) - raiseValidateError("Input to inv() must be square matrix -- given: a " + in.getDim1() + "x" + in.getDim2() + " matrix.", conditional); - - output.setDimensions(in.getDim1(), in.getDim2()); - output.setBlocksize(in.getBlocksize()); - break; - - case CHOLESKY: - { - // A = L%*%t(L) where L is the lower triangular matrix - checkNumParameters(1); - checkMatrixParam(getFirstExpr()); - - output.setDataType(DataType.MATRIX); - output.setValueType(ValueType.FP64); - - Identifier inA = getFirstExpr().getOutput(); - if(inA.dimsKnown() && inA.getDim1() != inA.getDim2()) - raiseValidateError("Input to cholesky() must be square matrix -- given: a " + inA.getDim1() + "x" + inA.getDim2() + " matrix.", conditional); - - output.setDimensions(inA.getDim1(), inA.getDim2()); - output.setBlocksize(inA.getBlocksize()); - break; - } + // set output characteristics + output.setDataType(id.getDataType()); + output.setDimensions(id.getDim1(), id2.getDim2()); + output.setBlocksize(id.getBlocksize()); + break; - case OUTER: - Identifier id2 = this.getSecondExpr().getOutput(); - - //check input types and characteristics - checkNumParameters(3); - checkMatrixParam(getFirstExpr()); - checkMatrixParam(getSecondExpr()); - checkScalarParam(getThirdExpr()); - checkValueTypeParam(getThirdExpr(), ValueType.STRING); - if( id.getDim2() > 1 || id2.getDim1()>1 ) { - raiseValidateError("Outer vector operations require a common dimension of one: " + - id.getDim1()+"x"+id.getDim2()+" o "+id2.getDim1()+"x"+id2.getDim2()+".", false); - } - - //set output characteristics - output.setDataType(id.getDataType()); - output.setDimensions(id.getDim1(), id2.getDim2()); - output.setBlocksize(id.getBlocksize()); - break; - - case BIASADD: - case BIASMULT: - { - Expression input = _args[0]; - Expression bias = _args[1]; - output.setDataType(DataType.MATRIX); - output.setValueType(ValueType.FP64); - output.setDimensions(input.getOutput().getDim1(), input.getOutput().getDim2()); - output.setBlocksize(input.getOutput().getBlocksize()); - checkMatrixParam(input); - checkMatrixParam(bias); - break; - } - case CONV2D: - case CONV2D_BACKWARD_FILTER: - case CONV2D_BACKWARD_DATA: - case MAX_POOL: - case AVG_POOL: - case MAX_POOL_BACKWARD: - case AVG_POOL_BACKWARD: - { - // At DML level: - // output = conv2d(input, filter, input_shape=[1, 3, 2, 2], filter_shape=[1, 3, 2, 2], - // strides=[1, 1], padding=[1,1]) - // - // Converted to following in constructor (only supported NCHW): - // output = conv2d(input, filter, stride1, stride2, padding1,padding2, - // input_shape1, input_shape2, input_shape3, input_shape4, - // filter_shape1, filter_shape2, filter_shape3, filter_shape4) - // - // Similarly, - // conv2d_backward_filter and conv2d_backward_data - Expression input = _args[0]; // For conv2d_backward_filter, this is input and for conv2d_backward_data, this is filter - - Expression input2 = null; - if(!(this.getOpCode() == Builtins.MAX_POOL || this.getOpCode() == Builtins.AVG_POOL)) { - input2 = _args[1]; // For conv2d_backward functions, this is dout - checkMatrixParam(input2); - } - output.setDataType(DataType.MATRIX); - output.setValueType(ValueType.FP64); - output.setBlocksize(input.getOutput().getBlocksize()); - - if(this.getOpCode() == Builtins.MAX_POOL_BACKWARD || this.getOpCode() == Builtins.AVG_POOL_BACKWARD) { + case BIASADD: + case BIASMULT: { + Expression input = _args[0]; + Expression bias = _args[1]; + output.setDataType(DataType.MATRIX); + output.setValueType(ValueType.FP64); output.setDimensions(input.getOutput().getDim1(), input.getOutput().getDim2()); + output.setBlocksize(input.getOutput().getBlocksize()); + checkMatrixParam(input); + checkMatrixParam(bias); + break; } - else { - // stride1, stride2, padding1, padding2, numImg, numChannels, imgSize, imgSize, - // filter_shape1=1, filter_shape2=1, filterSize/poolSize1, filterSize/poolSize1 - try { - int start = 2; - if(!(this.getOpCode() == Builtins.MAX_POOL || this.getOpCode() == Builtins.AVG_POOL)) { - start = 1; - } - long stride_h = (long) getDoubleValue(_args[start++]); - long stride_w = (long) getDoubleValue(_args[start++]); - long pad_h = (long) getDoubleValue(_args[start++]); - long pad_w = (long) getDoubleValue(_args[start++]); - long N = (long) getDoubleValue(_args[start++]); - long C = (long) getDoubleValue(_args[start++]); - long H = (long) getDoubleValue(_args[start++]); - long W = (long) getDoubleValue(_args[start++]); - long K = -1; - if(!(this.getOpCode() == Builtins.MAX_POOL || this.getOpCode() == Builtins.AVG_POOL)) { - K = (long) getDoubleValue(_args[start]); - } - start++; start++; // Increment index for K and C - long R = (long) getDoubleValue(_args[start++]); - long S = (long) getDoubleValue(_args[start++]); - - if(this.getOpCode() == Builtins.CONV2D_BACKWARD_FILTER) { - output.setDimensions(K, C*R*S); - } - else if(this.getOpCode() == Builtins.CONV2D_BACKWARD_DATA) { - output.setDimensions(N, C*H*W); - } - else if(H > 0 && W > 0 && stride_h > 0 && stride_w > 0 && pad_h >= 0 && pad_w >= 0 && R > 0 && S > 0) { - long P = DnnUtils.getP(H, R, stride_h, pad_h); - long Q = DnnUtils.getQ(W, S, stride_w, pad_w); - - // Try to set both rows and columns - if(this.getOpCode() == Builtins.CONV2D) - output.setDimensions(N, K*P*Q); - else if(this.getOpCode() == Builtins.MAX_POOL || this.getOpCode() == Builtins.AVG_POOL) - output.setDimensions(N, C*P*Q); - else - throw new LanguageException(""); - } - else { - // Since columns cannot be computed, set only rows - if(this.getOpCode() == Builtins.CONV2D) - output.setDimensions(input.getOutput().getDim1(), -1); - else if(this.getOpCode() == Builtins.MAX_POOL || this.getOpCode() == Builtins.AVG_POOL) - output.setDimensions(input.getOutput().getDim1(), -1); - else - throw new LanguageException(""); - } + case CONV2D: + case CONV2D_BACKWARD_FILTER: + case CONV2D_BACKWARD_DATA: + case MAX_POOL: + case AVG_POOL: + case MAX_POOL_BACKWARD: + case AVG_POOL_BACKWARD: { + // At DML level: + // output = conv2d(input, filter, input_shape=[1, 3, 2, 2], filter_shape=[1, 3, + // 2, 2], + // strides=[1, 1], padding=[1,1]) + // + // Converted to following in constructor (only supported NCHW): + // output = conv2d(input, filter, stride1, stride2, padding1,padding2, + // input_shape1, input_shape2, input_shape3, input_shape4, + // filter_shape1, filter_shape2, filter_shape3, filter_shape4) + // + // Similarly, + // conv2d_backward_filter and conv2d_backward_data + Expression input = _args[0]; // For conv2d_backward_filter, this is input and for conv2d_backward_data, + // this is filter + + Expression input2 = null; + if (!(this.getOpCode() == Builtins.MAX_POOL || this.getOpCode() == Builtins.AVG_POOL)) { + input2 = _args[1]; // For conv2d_backward functions, this is dout + checkMatrixParam(input2); } - catch(Exception e) { - output.setDimensions(-1, -1); // To make sure that output dimensions are not incorrect even if getDoubleValue doesnot return value + output.setDataType(DataType.MATRIX); + output.setValueType(ValueType.FP64); + output.setBlocksize(input.getOutput().getBlocksize()); + + if (this.getOpCode() == Builtins.MAX_POOL_BACKWARD || this.getOpCode() == Builtins.AVG_POOL_BACKWARD) { + output.setDimensions(input.getOutput().getDim1(), input.getOutput().getDim2()); + } else { + // stride1, stride2, padding1, padding2, numImg, numChannels, imgSize, imgSize, + // filter_shape1=1, filter_shape2=1, filterSize/poolSize1, filterSize/poolSize1 + try { + int start = 2; + if (!(this.getOpCode() == Builtins.MAX_POOL || this.getOpCode() == Builtins.AVG_POOL)) { + start = 1; + } + long stride_h = (long) getDoubleValue(_args[start++]); + long stride_w = (long) getDoubleValue(_args[start++]); + long pad_h = (long) getDoubleValue(_args[start++]); + long pad_w = (long) getDoubleValue(_args[start++]); + long N = (long) getDoubleValue(_args[start++]); + long C = (long) getDoubleValue(_args[start++]); + long H = (long) getDoubleValue(_args[start++]); + long W = (long) getDoubleValue(_args[start++]); + long K = -1; + if (!(this.getOpCode() == Builtins.MAX_POOL || this.getOpCode() == Builtins.AVG_POOL)) { + K = (long) getDoubleValue(_args[start]); + } + start++; + start++; // Increment index for K and C + long R = (long) getDoubleValue(_args[start++]); + long S = (long) getDoubleValue(_args[start++]); + + if (this.getOpCode() == Builtins.CONV2D_BACKWARD_FILTER) { + output.setDimensions(K, C * R * S); + } else if (this.getOpCode() == Builtins.CONV2D_BACKWARD_DATA) { + output.setDimensions(N, C * H * W); + } else if (H > 0 && W > 0 && stride_h > 0 && stride_w > 0 && pad_h >= 0 && pad_w >= 0 && R > 0 + && S > 0) { + long P = DnnUtils.getP(H, R, stride_h, pad_h); + long Q = DnnUtils.getQ(W, S, stride_w, pad_w); + + // Try to set both rows and columns + if (this.getOpCode() == Builtins.CONV2D) + output.setDimensions(N, K * P * Q); + else if (this.getOpCode() == Builtins.MAX_POOL || this.getOpCode() == Builtins.AVG_POOL) + output.setDimensions(N, C * P * Q); + else + throw new LanguageException(""); + } else { + // Since columns cannot be computed, set only rows + if (this.getOpCode() == Builtins.CONV2D) + output.setDimensions(input.getOutput().getDim1(), -1); + else if (this.getOpCode() == Builtins.MAX_POOL || this.getOpCode() == Builtins.AVG_POOL) + output.setDimensions(input.getOutput().getDim1(), -1); + else + throw new LanguageException(""); + } + } catch (Exception e) { + output.setDimensions(-1, -1); // To make sure that output dimensions are not incorrect even if + // getDoubleValue doesnot return value + } } + checkMatrixParam(input); + if (input2 != null) + checkMatrixParam(input2); + break; } - checkMatrixParam(input); - if(input2 != null) - checkMatrixParam(input2); - break; - } - case TIME: - checkNumParameters(0); - // Output of TIME() is scalar and long - output.setDataType(DataType.SCALAR); - output.setValueType(ValueType.INT64); - output.setDimensions(0, 0); - output.setBlocksize(0); - break; - - case DROP_INVALID_TYPE: - case VALUE_SWAP: - case FRAME_ROW_REPLICATE: - checkNumParameters(2); - checkMatrixFrameParam(getFirstExpr()); - checkMatrixFrameParam(getSecondExpr()); - output.setDataType(DataType.FRAME); - output.setDimensions(id.getDim1(), id.getDim2()); - output.setBlocksize (id.getBlocksize()); - output.setValueType(ValueType.STRING); - break; - - case DROP_INVALID_LENGTH: - checkNumParameters(2); - checkMatrixFrameParam(getFirstExpr()); - checkMatrixFrameParam(getSecondExpr()); - output.setDataType(DataType.FRAME); - output.setDimensions(id.getDim1(), id.getDim2()); - output.setBlocksize (id.getBlocksize()); - output.setValueType(id.getValueType()); - break; - case APPLY_SCHEMA: + case TIME: + checkNumParameters(0); + // Output of TIME() is scalar and long + output.setDataType(DataType.SCALAR); + output.setValueType(ValueType.INT64); + output.setDimensions(0, 0); + output.setBlocksize(0); + break; + + case DROP_INVALID_TYPE: + case VALUE_SWAP: + case FRAME_ROW_REPLICATE: checkNumParameters(2); checkMatrixFrameParam(getFirstExpr()); checkMatrixFrameParam(getSecondExpr()); output.setDataType(DataType.FRAME); output.setDimensions(id.getDim1(), id.getDim2()); - output.setBlocksize (id.getBlocksize()); - break; - case MAP: - checkNumParameters(getThirdExpr() != null ? 3 : 2); - checkMatrixFrameParam(getFirstExpr()); - checkScalarParam(getSecondExpr()); - if(getThirdExpr() != null) - checkScalarParam(getThirdExpr()); // margin - output.setDataType(DataType.FRAME); - if(_args[1].getText().contains("jaccardSim")) { - output.setDimensions(id.getDim1(), id.getDim1()); - output.setValueType(ValueType.FP64); - } - else { - output.setDimensions(id.getDim1(), id.getDim2()); + output.setBlocksize(id.getBlocksize()); output.setValueType(ValueType.STRING); - } - break; - case LOCAL: - if(OptimizerUtils.ALLOW_SCRIPT_LEVEL_LOCAL_COMMAND){ + break; + + case DROP_INVALID_LENGTH: + checkNumParameters(2); + checkMatrixFrameParam(getFirstExpr()); + checkMatrixFrameParam(getSecondExpr()); + output.setDataType(DataType.FRAME); + output.setDimensions(id.getDim1(), id.getDim2()); + output.setBlocksize(id.getBlocksize()); + output.setValueType(id.getValueType()); + break; + case APPLY_SCHEMA: + checkNumParameters(2); + checkMatrixFrameParam(getFirstExpr()); + checkMatrixFrameParam(getSecondExpr()); + output.setDataType(DataType.FRAME); + output.setDimensions(id.getDim1(), id.getDim2()); + output.setBlocksize(id.getBlocksize()); + break; + case MAP: + checkNumParameters(getThirdExpr() != null ? 3 : 2); + checkMatrixFrameParam(getFirstExpr()); + checkScalarParam(getSecondExpr()); + if (getThirdExpr() != null) + checkScalarParam(getThirdExpr()); // margin + output.setDataType(DataType.FRAME); + if (_args[1].getText().contains("jaccardSim")) { + output.setDimensions(id.getDim1(), id.getDim1()); + output.setValueType(ValueType.FP64); + } else { + output.setDimensions(id.getDim1(), id.getDim2()); + output.setValueType(ValueType.STRING); + } + break; + case LOCAL: + if (OptimizerUtils.ALLOW_SCRIPT_LEVEL_LOCAL_COMMAND) { + checkNumParameters(1); + checkMatrixParam(getFirstExpr()); + output.setDataType(DataType.MATRIX); + output.setDimensions(id.getDim1(), id.getDim2()); + output.setBlocksize(id.getBlocksize()); + output.setValueType(id.getValueType()); + } else + raiseValidateError("Local instruction not allowed in dml script"); + case COMPRESS: + case DECOMPRESS: + if (OptimizerUtils.ALLOW_SCRIPT_LEVEL_COMPRESS_COMMAND) { + checkNumParameters(1); + checkMatrixParam(getFirstExpr()); + output.setDataType(DataType.MATRIX); + output.setDimensions(id.getDim1(), id.getDim2()); + output.setBlocksize(id.getBlocksize()); + output.setValueType(id.getValueType()); + } else + raiseValidateError("Compress/DeCompress instruction not allowed in dml script"); + break; + case ROW_COUNT_DISTINCT: checkNumParameters(1); checkMatrixParam(getFirstExpr()); output.setDataType(DataType.MATRIX); - output.setDimensions(id.getDim1(), id.getDim2()); - output.setBlocksize (id.getBlocksize()); - output.setValueType(id.getValueType()); - } - else - raiseValidateError("Local instruction not allowed in dml script"); - case COMPRESS: - case DECOMPRESS: - if(OptimizerUtils.ALLOW_SCRIPT_LEVEL_COMPRESS_COMMAND){ + output.setDimensions(id.getDim1(), 1); + output.setBlocksize(id.getBlocksize()); + output.setValueType(ValueType.INT64); + output.setNnz(id.getDim1()); + break; + + case COL_COUNT_DISTINCT: checkNumParameters(1); checkMatrixParam(getFirstExpr()); output.setDataType(DataType.MATRIX); - output.setDimensions(id.getDim1(), id.getDim2()); - output.setBlocksize (id.getBlocksize()); - output.setValueType(id.getValueType()); - } - else - raiseValidateError("Compress/DeCompress instruction not allowed in dml script"); - break; - case ROW_COUNT_DISTINCT: - checkNumParameters(1); - checkMatrixParam(getFirstExpr()); - output.setDataType(DataType.MATRIX); - output.setDimensions(id.getDim1(), 1); - output.setBlocksize (id.getBlocksize()); - output.setValueType(ValueType.INT64); - output.setNnz(id.getDim1()); - break; - - case COL_COUNT_DISTINCT: - checkNumParameters(1); - checkMatrixParam(getFirstExpr()); - output.setDataType(DataType.MATRIX); - output.setDimensions(1, id.getDim2()); - output.setBlocksize (id.getBlocksize()); - output.setValueType(ValueType.INT64); - output.setNnz(id.getDim2()); - break; - - default: - if( isMathFunction() ) { - checkMathFunctionParam(); - //unary operations - if( getSecondExpr() == null ) { - output.setDataType(id.getDataType()); - output.setValueType((output.getDataType()==DataType.SCALAR - && getOpCode()==Builtins.ABS)?id.getValueType():ValueType.FP64 ); - output.setDimensions(id.getDim1(), id.getDim2()); - output.setBlocksize(id.getBlocksize()); - } - //binary operations - else { - setBinaryOutputProperties(output); - // override computed value type for special cases - if( getOpCode() == Builtins.LOG ) - output.setValueType(ValueType.FP64); + output.setDimensions(1, id.getDim2()); + output.setBlocksize(id.getBlocksize()); + output.setValueType(ValueType.INT64); + output.setNnz(id.getDim2()); + break; + + default: + if (isMathFunction()) { + checkMathFunctionParam(); + // unary operations + if (getSecondExpr() == null) { + output.setDataType(id.getDataType()); + output.setValueType((output.getDataType() == DataType.SCALAR + && getOpCode() == Builtins.ABS) ? id.getValueType() : ValueType.FP64); + output.setDimensions(id.getDim1(), id.getDim2()); + output.setBlocksize(id.getBlocksize()); + } + // binary operations + else { + setBinaryOutputProperties(output); + // override computed value type for special cases + if (getOpCode() == Builtins.LOG) + output.setValueType(ValueType.FP64); + } + } else { + // always unconditional (because unsupported operation) + Builtins op = getOpCode(); + if (op == Builtins.EIGEN || op == Builtins.LU || op == Builtins.QR || op == Builtins.SVD + || op == Builtins.LSTM || op == Builtins.LSTM_BACKWARD + || op == Builtins.BATCH_NORM2D || op == Builtins.BATCH_NORM2D_BACKWARD) + raiseValidateError("Function " + op + " needs to be called with multi-return assignment.", + false, LanguageErrorCodes.INVALID_PARAMETERS); + else + raiseValidateError("Unsupported function " + op, false, LanguageErrorCodes.INVALID_PARAMETERS); } - } - else { - // always unconditional (because unsupported operation) - Builtins op = getOpCode(); - if( op==Builtins.EIGEN || op==Builtins.LU || op==Builtins.QR || op==Builtins.SVD - || op==Builtins.LSTM || op==Builtins.LSTM_BACKWARD - || op==Builtins.BATCH_NORM2D || op==Builtins.BATCH_NORM2D_BACKWARD) - raiseValidateError("Function "+op+" needs to be called with multi-return assignment.", false, LanguageErrorCodes.INVALID_PARAMETERS); - else - raiseValidateError("Unsupported function "+op, false, LanguageErrorCodes.INVALID_PARAMETERS); - } } } private void setBinaryOutputProperties(DataIdentifier output) { DataType dt1 = getFirstExpr().getOutput().getDataType(); DataType dt2 = getSecondExpr().getOutput().getDataType(); - DataType dtOut = (dt1==DataType.MATRIX || dt2==DataType.MATRIX) ? - DataType.MATRIX : DataType.SCALAR; - if( dt1==DataType.MATRIX && dt2==DataType.MATRIX ) + DataType dtOut = (dt1 == DataType.MATRIX || dt2 == DataType.MATRIX) ? DataType.MATRIX : DataType.SCALAR; + if (dt1 == DataType.MATRIX && dt2 == DataType.MATRIX) checkMatchingDimensions(getFirstExpr(), getSecondExpr(), true); MatrixCharacteristics dims = getBinaryMatrixCharacteristics(getFirstExpr(), getSecondExpr()); output.setDataType(dtOut); - output.setValueType(dtOut==DataType.MATRIX ? ValueType.FP64 : - computeValueType(getFirstExpr(), getSecondExpr(), true)); + output.setValueType( + dtOut == DataType.MATRIX ? ValueType.FP64 : computeValueType(getFirstExpr(), getSecondExpr(), true)); output.setDimensions(dims.getRows(), dims.getCols()); - output.setBlocksize (dims.getBlocksize()); + output.setBlocksize(dims.getBlocksize()); } - + private void setTernaryOutputProperties(DataIdentifier output, boolean conditional) { DataType dt1 = getFirstExpr().getOutput().getDataType(); DataType dt2 = getSecondExpr().getOutput().getDataType(); DataType dt3 = getThirdExpr().getOutput().getDataType(); - DataType dtOut = (dt1.isMatrix() || dt2.isMatrix() || dt3.isMatrix()) ? - DataType.MATRIX : DataType.SCALAR; - if( dt1==DataType.MATRIX && dt2==DataType.MATRIX ) + DataType dtOut = (dt1.isMatrix() || dt2.isMatrix() || dt3.isMatrix()) ? DataType.MATRIX : DataType.SCALAR; + if (dt1 == DataType.MATRIX && dt2 == DataType.MATRIX) checkMatchingDimensions(getFirstExpr(), getSecondExpr(), false, conditional); - if( dt1==DataType.MATRIX && dt3==DataType.MATRIX ) + if (dt1 == DataType.MATRIX && dt3 == DataType.MATRIX) checkMatchingDimensions(getFirstExpr(), getThirdExpr(), false, conditional); - if( dt2==DataType.MATRIX && dt3==DataType.MATRIX ) + if (dt2 == DataType.MATRIX && dt3 == DataType.MATRIX) checkMatchingDimensions(getSecondExpr(), getThirdExpr(), false, conditional); MatrixCharacteristics dims1 = getBinaryMatrixCharacteristics(getFirstExpr(), getSecondExpr()); MatrixCharacteristics dims2 = getBinaryMatrixCharacteristics(getSecondExpr(), getThirdExpr()); output.setDataType(dtOut); - output.setValueType(dtOut==DataType.MATRIX ? ValueType.FP64 : - computeValueType(getSecondExpr(), getThirdExpr(), true)); + output.setValueType( + dtOut == DataType.MATRIX ? ValueType.FP64 : computeValueType(getSecondExpr(), getThirdExpr(), true)); output.setDimensions(Math.max(dims1.getRows(), dims2.getRows()), Math.max(dims1.getCols(), dims2.getCols())); output.setBlocksize(Math.max(dims1.getBlocksize(), dims2.getBlocksize())); } private void setNaryOutputProperties(DataIdentifier output) { DataType dt = Arrays.stream(getAllExpr()).allMatch( - e -> e.getOutput().getDataType().isScalar()) ? DataType.SCALAR : DataType.MATRIX; + e -> e.getOutput().getDataType().isScalar()) ? DataType.SCALAR : DataType.MATRIX; Expression firstM = dt.isMatrix() ? Arrays.stream(getAllExpr()).filter( - e -> e.getOutput().getDataType().isMatrix()).findFirst().get() : null; + e -> e.getOutput().getDataType().isMatrix()).findFirst().get() : null; ValueType vt = dt.isMatrix() ? ValueType.FP64 : ValueType.INT64; - for( Expression e : getAllExpr() ) { + for (Expression e : getAllExpr()) { vt = computeValueType(e, e.getOutput().getValueType(), vt, true); - if( e.getOutput().getDataType().isMatrix() ) + if (e.getOutput().getDataType().isMatrix()) checkMatchingDimensions(firstM, e, true); } output.setDataType(dt); output.setValueType(vt); output.setDimensions(dt.isMatrix() ? firstM.getOutput().getDim1() : 0, - dt.isMatrix() ? firstM.getOutput().getDim2() : 0); - output.setBlocksize (dt.isMatrix() ? firstM.getOutput().getBlocksize() : 0); + dt.isMatrix() ? firstM.getOutput().getDim2() : 0); + output.setBlocksize(dt.isMatrix() ? firstM.getOutput().getBlocksize() : 0); } - + private void expandArguments() { - - if ( _args == null ) { + + if (_args == null) { _args = new Expression[1]; return; } - Expression [] temp = _args.clone(); + Expression[] temp = _args.clone(); _args = new Expression[_args.length + 1]; System.arraycopy(temp, 0, _args, 0, temp.length); } - + @Override public boolean multipleReturns() { return _opcode.isMultiReturn(); } private static boolean isConstant(Expression expr) { - return ( expr != null && expr instanceof ConstIdentifier ); + return (expr != null && expr instanceof ConstIdentifier); } - + private static double getDoubleValue(Expression expr) { - if ( expr instanceof DoubleIdentifier ) - return ((DoubleIdentifier)expr).getValue(); - else if ( expr instanceof IntIdentifier) - return ((IntIdentifier)expr).getValue(); + if (expr instanceof DoubleIdentifier) + return ((DoubleIdentifier) expr).getValue(); + else if (expr instanceof IntIdentifier) + return ((IntIdentifier) expr).getValue(); else throw new LanguageException("Expecting a numeric value."); } - + private boolean isMathFunction() { switch (this.getOpCode()) { - case COS: - case SIN: - case TAN: - case ACOS: - case ASIN: - case ATAN: - case COSH: - case SINH: - case TANH: - case SIGN: - case SQRT: - case ABS: - case LOG: - case EXP: - case ROUND: - case CEIL: - case FLOOR: - case MEDIAN: - case XOR: - case BITWAND: - case BITWOR: - case BITWXOR: - case BITWSHIFTL: - case BITWSHIFTR: - return true; - default: - return false; + case COS: + case SIN: + case TAN: + case ACOS: + case ASIN: + case ATAN: + case COSH: + case SINH: + case TANH: + case SIGN: + case SQRT: + case ABS: + case LOG: + case EXP: + case ROUND: + case CEIL: + case FLOOR: + case MEDIAN: + case XOR: + case BITWAND: + case BITWOR: + case BITWXOR: + case BITWSHIFTL: + case BITWSHIFTR: + return true; + default: + return false; } } private void checkMathFunctionParam() { switch (this.getOpCode()) { - case COS: - case SIN: - case TAN: - case ACOS: - case ASIN: - case ATAN: - case COSH: - case SINH: - case TANH: - case SIGN: - case SQRT: - case ABS: - case EXP: - case ROUND: - case CEIL: - case FLOOR: - case MEDIAN: - checkNumParameters(1); - break; - case LOG: - if (getSecondExpr() != null) { - checkNumParameters(2); - } - else { - checkNumParameters(1); - } - break; - default: - //always unconditional - raiseValidateError("Unknown math function "+ this.getOpCode(), false); + case COS: + case SIN: + case TAN: + case ACOS: + case ASIN: + case ATAN: + case COSH: + case SINH: + case TANH: + case SIGN: + case SQRT: + case ABS: + case EXP: + case ROUND: + case CEIL: + case FLOOR: + case MEDIAN: + checkNumParameters(1); + break; + case LOG: + if (getSecondExpr() != null) { + checkNumParameters(2); + } else { + checkNumParameters(1); + } + break; + default: + // always unconditional + raiseValidateError("Unknown math function " + this.getOpCode(), false); } } @@ -1927,11 +1950,11 @@ public String toString() { // third part of expression IS NOT a variable -- it is the OP to be applied public VariableSet variablesRead() { VariableSet result = new VariableSet(); - - for(int i=0; i<_args.length; i++) { + + for (int i = 0; i < _args.length; i++) { result.addVariables(_args[i].variablesRead()); } - + return result; } @@ -1942,13 +1965,14 @@ public VariableSet variablesUpdated() { return result; } - protected void checkNumParameters(int count) { //always unconditional + protected void checkNumParameters(int count) { // always unconditional if (getFirstExpr() == null && _args.length > 0) { raiseValidateError("Missing argument for function " + this.getOpCode(), false, LanguageErrorCodes.INVALID_PARAMETERS); } - // Not sure the rationale for the first two if loops, but will keep them for backward compatibility + // Not sure the rationale for the first two if loops, but will keep them for + // backward compatibility if (((count == 1) && (getSecondExpr() != null || getThirdExpr() != null)) || ((count == 2) && (getThirdExpr() != null))) { raiseValidateError("Invalid number of arguments for function " + this.getOpCode().toString().toLowerCase() @@ -1957,7 +1981,7 @@ protected void checkNumParameters(int count) { //always unconditional || ((count == 3) && (getSecondExpr() == null || getThirdExpr() == null))) { raiseValidateError("Missing argument for function " + this.getOpCode(), false, LanguageErrorCodes.INVALID_PARAMETERS); - } else if(count > 0 && (_args == null || _args.length < count)) { + } else if (count > 0 && (_args == null || _args.length < count)) { raiseValidateError("Missing argument for function " + this.getOpCode(), false, LanguageErrorCodes.INVALID_PARAMETERS); } else if (count == 0 && (_args.length > 0 @@ -1973,7 +1997,7 @@ protected void checkMatrixParam(Expression e) { this.getOpCode().toString().toLowerCase() + "().", false); } } - + protected void checkMatrixTensorParam(Expression e) { if (e.getOutput().getDataType() != DataType.MATRIX) { // Param is not a matrix @@ -1986,137 +2010,145 @@ protected void checkMatrixTensorParam(Expression e) { } } - protected void checkDataTypeParam(Expression e, DataType... dt) { //always unconditional - if( !ArrayUtils.contains(dt, e.getOutput().getDataType()) ) - raiseValidateError("Non-matching expected data type for function "+ getOpCode(), false, LanguageErrorCodes.UNSUPPORTED_PARAMETERS); + protected void checkDataTypeParam(Expression e, DataType... dt) { // always unconditional + if (!ArrayUtils.contains(dt, e.getOutput().getDataType())) + raiseValidateError("Non-matching expected data type for function " + getOpCode(), false, + LanguageErrorCodes.UNSUPPORTED_PARAMETERS); } - protected void checkMatrixFrameParam(Expression e) { //always unconditional + protected void checkMatrixFrameParam(Expression e) { // always unconditional if (e.getOutput().getDataType() != DataType.MATRIX && e.getOutput().getDataType() != DataType.FRAME) { - raiseValidateError("Expecting matrix or frame parameter for function "+ getOpCode(), false, LanguageErrorCodes.UNSUPPORTED_PARAMETERS); + raiseValidateError("Expecting matrix or frame parameter for function " + getOpCode(), false, + LanguageErrorCodes.UNSUPPORTED_PARAMETERS); } } - - protected void checkMatrixScalarParam(Expression e) { //always unconditional + + protected void checkMatrixScalarParam(Expression e) { // always unconditional if (e.getOutput().getDataType() != DataType.MATRIX && e.getOutput().getDataType() != DataType.SCALAR) { - raiseValidateError("Expecting matrix or scalar parameter for function "+ getOpCode(), false, LanguageErrorCodes.UNSUPPORTED_PARAMETERS); + raiseValidateError("Expecting matrix or scalar parameter for function " + getOpCode(), false, + LanguageErrorCodes.UNSUPPORTED_PARAMETERS); } } - - private void checkScalarParam(Expression e) { //always unconditional + + private void checkScalarParam(Expression e) { // always unconditional if (e.getOutput().getDataType() != DataType.SCALAR) { - raiseValidateError("Expecting scalar parameter for function " + getOpCode(), false, LanguageErrorCodes.UNSUPPORTED_PARAMETERS); + raiseValidateError("Expecting scalar parameter for function " + getOpCode(), false, + LanguageErrorCodes.UNSUPPORTED_PARAMETERS); } } - - private void checkListParam(Expression e) { //always unconditional + + private void checkListParam(Expression e) { // always unconditional if (e.getOutput().getDataType() != DataType.LIST) { - raiseValidateError("Expecting scalar parameter for function " + getOpCode(), false, LanguageErrorCodes.UNSUPPORTED_PARAMETERS); + raiseValidateError("Expecting scalar parameter for function " + getOpCode(), false, + LanguageErrorCodes.UNSUPPORTED_PARAMETERS); } } - + @SuppressWarnings("unused") - private void checkScalarFrameParam(Expression e) { //always unconditional + private void checkScalarFrameParam(Expression e) { // always unconditional if (e.getOutput().getDataType() != DataType.SCALAR && e.getOutput().getDataType() != DataType.FRAME) { - raiseValidateError("Expecting scalar parameter for function " + getOpCode(), false, LanguageErrorCodes.UNSUPPORTED_PARAMETERS); + raiseValidateError("Expecting scalar parameter for function " + getOpCode(), false, + LanguageErrorCodes.UNSUPPORTED_PARAMETERS); } } - private void checkValueTypeParam(Expression e, ValueType vt) { //always unconditional + private void checkValueTypeParam(Expression e, ValueType vt) { // always unconditional if (e.getOutput().getValueType() != vt) { - raiseValidateError("Expecting parameter of different value type " + this.getOpCode(), false, LanguageErrorCodes.UNSUPPORTED_PARAMETERS); + raiseValidateError("Expecting parameter of different value type " + this.getOpCode(), false, + LanguageErrorCodes.UNSUPPORTED_PARAMETERS); } } - - protected void checkStringOrDataIdentifier(Expression e) { //always unconditional - if( !(e.getOutput().getDataType().isScalar() && e.getOutput().getValueType()==ValueType.STRING) - && !(e instanceof DataIdentifier && !(e instanceof IndexedIdentifier)) ) { - raiseValidateError("Expecting variable name or data identifier "+ getOpCode(), false, LanguageErrorCodes.UNSUPPORTED_PARAMETERS); + + protected void checkStringOrDataIdentifier(Expression e) { // always unconditional + if (!(e.getOutput().getDataType().isScalar() && e.getOutput().getValueType() == ValueType.STRING) + && !(e instanceof DataIdentifier && !(e instanceof IndexedIdentifier))) { + raiseValidateError("Expecting variable name or data identifier " + getOpCode(), false, + LanguageErrorCodes.UNSUPPORTED_PARAMETERS); } } - + private static boolean is1DMatrix(Expression e) { - return (e.getOutput().getDim1() == 1 || e.getOutput().getDim2() == 1 ); + return (e.getOutput().getDim1() == 1 || e.getOutput().getDim2() == 1); } - + private static boolean dimsKnown(Expression e) { return (e.getOutput().getDim1() != -1 && e.getOutput().getDim2() != -1); } - - private void check1DMatrixParam(Expression e) { //always unconditional + + private void check1DMatrixParam(Expression e) { // always unconditional checkMatrixParam(e); - - // throw an exception, when e's output is NOT a one-dimensional matrix - // the check must be performed only when the dimensions are known at compilation time - if ( dimsKnown(e) && !is1DMatrix(e)) { + + // throw an exception, when e's output is NOT a one-dimensional matrix + // the check must be performed only when the dimensions are known at compilation + // time + if (dimsKnown(e) && !is1DMatrix(e)) { raiseValidateError("Expecting one-dimensional matrix parameter for function " - + this.getOpCode(), false, LanguageErrorCodes.UNSUPPORTED_PARAMETERS); + + this.getOpCode(), false, LanguageErrorCodes.UNSUPPORTED_PARAMETERS); } } private void checkMatchingDimensions(Expression expr1, Expression expr2) { checkMatchingDimensions(expr1, expr2, false); } - + private void checkMatchingDimensions(Expression expr1, Expression expr2, boolean allowsMV) { checkMatchingDimensions(expr1, expr2, allowsMV, false); } - - private void checkMatchingDimensions(Expression expr1, Expression expr2, boolean allowsMV, boolean conditional) - { + + private void checkMatchingDimensions(Expression expr1, Expression expr2, boolean allowsMV, boolean conditional) { if (expr1 != null && expr2 != null) { - + // if any matrix has unknown dimensions, simply return - if( expr1.getOutput().getDim1() == -1 || expr2.getOutput().getDim1() == -1 - ||expr1.getOutput().getDim2() == -1 || expr2.getOutput().getDim2() == -1 ) - { + if (expr1.getOutput().getDim1() == -1 || expr2.getOutput().getDim1() == -1 + || expr1.getOutput().getDim2() == -1 || expr2.getOutput().getDim2() == -1) { return; - } - else if( (!allowsMV && expr1.getOutput().getDim1() != expr2.getOutput().getDim1()) - || (allowsMV && expr1.getOutput().getDim1() != expr2.getOutput().getDim1() && expr2.getOutput().getDim1() != 1) - || (!allowsMV && expr1.getOutput().getDim2() != expr2.getOutput().getDim2()) - || (allowsMV && expr1.getOutput().getDim2() != expr2.getOutput().getDim2() && expr2.getOutput().getDim2() != 1) ) - { + } else if ((!allowsMV && expr1.getOutput().getDim1() != expr2.getOutput().getDim1()) + || (allowsMV && expr1.getOutput().getDim1() != expr2.getOutput().getDim1() + && expr2.getOutput().getDim1() != 1) + || (!allowsMV && expr1.getOutput().getDim2() != expr2.getOutput().getDim2()) + || (allowsMV && expr1.getOutput().getDim2() != expr2.getOutput().getDim2() + && expr2.getOutput().getDim2() != 1)) { raiseValidateError("Mismatch in matrix dimensions of parameters for function " + this.getOpCode(), conditional, LanguageErrorCodes.INVALID_PARAMETERS); } } } - - private void checkMatchingDimensionsQuantile() - { + + private void checkMatchingDimensionsQuantile() { if (getFirstExpr().getOutput().getDim1() != getSecondExpr().getOutput().getDim1()) { raiseValidateError("Mismatch in matrix dimensions for " + this.getOpCode(), false, LanguageErrorCodes.INVALID_PARAMETERS); } } - public static BuiltinFunctionExpression getBuiltinFunctionExpression(ParserRuleContext ctx, - String functionName, ArrayList paramExprsPassed, String filename) { - + public static BuiltinFunctionExpression getBuiltinFunctionExpression(ParserRuleContext ctx, + String functionName, ArrayList paramExprsPassed, String filename) { + if (functionName == null || paramExprsPassed == null) return null; - + // check if the function name is built-in function - // (assign built-in function op if function is built-in - - - return (Builtins.contains(functionName, false, false) - && (paramExprsPassed.stream().anyMatch(p -> p.getName()==null) //at least one unnamed - || paramExprsPassed.size() == 0)) ? - new BuiltinFunctionExpression(ctx, Builtins.get(functionName), paramExprsPassed, filename) : null; + // (assign built-in function op if function is built-in + + return (Builtins.contains(functionName, false, false) + && (paramExprsPassed.stream().anyMatch(p -> p.getName() == null) // at least one unnamed + || paramExprsPassed.size() == 0)) + ? new BuiltinFunctionExpression(ctx, Builtins.get(functionName), paramExprsPassed, + filename) + : null; } - + /** - * Convert a value type (double, int, or boolean) to a built-in function operator. + * Convert a value type (double, int, or boolean) to a built-in function + * operator. * - * @param vt Value type ({@code ValueType.DOUBLE}, {@code ValueType.INT}, or {@code ValueType.BOOLEAN}). + * @param vt Value type ({@code ValueType.DOUBLE}, {@code ValueType.INT}, or + * {@code ValueType.BOOLEAN}). * @return Built-in function operator ({@code Builtins.AS_DOUBLE}, - * {@code Builtins.AS_INT}, or {@code Builtins.AS_BOOLEAN}). + * {@code Builtins.AS_INT}, or {@code Builtins.AS_BOOLEAN}). */ - public static Builtins getValueTypeCastOperator( ValueType vt ) { - switch( vt ) - { + public static Builtins getValueTypeCastOperator(ValueType vt) { + switch (vt) { case FP64: return Builtins.CAST_AS_DOUBLE; case INT64: @@ -2124,7 +2156,7 @@ public static Builtins getValueTypeCastOperator( ValueType vt ) { case BOOLEAN: return Builtins.CAST_AS_BOOLEAN; default: - throw new LanguageException("No cast for value type "+vt); + throw new LanguageException("No cast for value type " + vt); } } } diff --git a/src/main/java/org/apache/sysds/runtime/instructions/CPInstructionParser.java b/src/main/java/org/apache/sysds/runtime/instructions/CPInstructionParser.java index a51711e40e3..aacbe73f7b8 100644 --- a/src/main/java/org/apache/sysds/runtime/instructions/CPInstructionParser.java +++ b/src/main/java/org/apache/sysds/runtime/instructions/CPInstructionParser.java @@ -56,6 +56,7 @@ import org.apache.sysds.runtime.instructions.cp.MMChainCPInstruction; import org.apache.sysds.runtime.instructions.cp.MMTSJCPInstruction; import org.apache.sysds.runtime.instructions.cp.MultiReturnBuiltinCPInstruction; +import org.apache.sysds.runtime.instructions.cp.MultiReturnMatrixMatrixBuiltinCPInstruction; import org.apache.sysds.runtime.instructions.cp.MultiReturnParameterizedBuiltinCPInstruction; import org.apache.sysds.runtime.instructions.cp.PMMJCPInstruction; import org.apache.sysds.runtime.instructions.cp.ParameterizedBuiltinCPInstruction; @@ -80,414 +81,417 @@ public class CPInstructionParser extends InstructionParser { public static final HashMap String2CPInstructionType; static { String2CPInstructionType = new HashMap<>(); - String2CPInstructionType.put( "ba+*" , CPType.AggregateBinary); - String2CPInstructionType.put( "tak+*" , CPType.AggregateTernary); - String2CPInstructionType.put( "tack+*" , CPType.AggregateTernary); - - String2CPInstructionType.put( "uak+" , CPType.AggregateUnary); - String2CPInstructionType.put( "uark+" , CPType.AggregateUnary); - String2CPInstructionType.put( "uack+" , CPType.AggregateUnary); - String2CPInstructionType.put( "uasqk+" , CPType.AggregateUnary); - String2CPInstructionType.put( "uarsqk+" , CPType.AggregateUnary); - String2CPInstructionType.put( "uacsqk+" , CPType.AggregateUnary); - String2CPInstructionType.put( "uamean" , CPType.AggregateUnary); - String2CPInstructionType.put( "uarmean" , CPType.AggregateUnary); - String2CPInstructionType.put( "uacmean" , CPType.AggregateUnary); - String2CPInstructionType.put( "uavar" , CPType.AggregateUnary); - String2CPInstructionType.put( "uarvar" , CPType.AggregateUnary); - String2CPInstructionType.put( "uacvar" , CPType.AggregateUnary); - String2CPInstructionType.put( "uamax" , CPType.AggregateUnary); - String2CPInstructionType.put( "uarmax" , CPType.AggregateUnary); - String2CPInstructionType.put( "uarimax" , CPType.AggregateUnary); - String2CPInstructionType.put( "uacmax" , CPType.AggregateUnary); - String2CPInstructionType.put( "uamin" , CPType.AggregateUnary); - String2CPInstructionType.put( "uarmin" , CPType.AggregateUnary); - String2CPInstructionType.put( "uarimin" , CPType.AggregateUnary); - String2CPInstructionType.put( "uacmin" , CPType.AggregateUnary); - String2CPInstructionType.put( "ua+" , CPType.AggregateUnary); - String2CPInstructionType.put( "uar+" , CPType.AggregateUnary); - String2CPInstructionType.put( "uac+" , CPType.AggregateUnary); - String2CPInstructionType.put( "ua*" , CPType.AggregateUnary); - String2CPInstructionType.put( "uar*" , CPType.AggregateUnary); - String2CPInstructionType.put( "uac*" , CPType.AggregateUnary); - String2CPInstructionType.put( "uatrace" , CPType.AggregateUnary); - String2CPInstructionType.put( "uaktrace", CPType.AggregateUnary); - String2CPInstructionType.put( "nrow" , CPType.AggregateUnary); - String2CPInstructionType.put( "ncol" , CPType.AggregateUnary); - String2CPInstructionType.put( "length" , CPType.AggregateUnary); - String2CPInstructionType.put( "exists" , CPType.AggregateUnary); - String2CPInstructionType.put( "lineage" , CPType.AggregateUnary); - String2CPInstructionType.put( "uacd" , CPType.AggregateUnary); - String2CPInstructionType.put( "uacdr" , CPType.AggregateUnary); - String2CPInstructionType.put( "uacdc" , CPType.AggregateUnary); - String2CPInstructionType.put( "uacdap" , CPType.AggregateUnary); - String2CPInstructionType.put( "uacdapr" , CPType.AggregateUnary); - String2CPInstructionType.put( "uacdapc" , CPType.AggregateUnary); - String2CPInstructionType.put( "unique" , CPType.AggregateUnary); - String2CPInstructionType.put( "uniquer" , CPType.AggregateUnary); - String2CPInstructionType.put( "uniquec" , CPType.AggregateUnary); - - String2CPInstructionType.put( "uaggouterchain", CPType.UaggOuterChain); - - // Arithmetic Instruction Opcodes - String2CPInstructionType.put( "+" , CPType.Binary); - String2CPInstructionType.put( "-" , CPType.Binary); - String2CPInstructionType.put( "*" , CPType.Binary); - String2CPInstructionType.put( "/" , CPType.Binary); - String2CPInstructionType.put( "%%" , CPType.Binary); - String2CPInstructionType.put( "%/%" , CPType.Binary); - String2CPInstructionType.put( "^" , CPType.Binary); - String2CPInstructionType.put( "1-*" , CPType.Binary); //special * case - String2CPInstructionType.put( "^2" , CPType.Binary); //special ^ case - String2CPInstructionType.put( "*2" , CPType.Binary); //special * case - String2CPInstructionType.put( "-nz" , CPType.Binary); //special - case - - // Boolean Instruction Opcodes - String2CPInstructionType.put( "&&" , CPType.Binary); - String2CPInstructionType.put( "||" , CPType.Binary); - String2CPInstructionType.put( "xor" , CPType.Binary); - String2CPInstructionType.put( "bitwAnd", CPType.Binary); - String2CPInstructionType.put( "bitwOr", CPType.Binary); - String2CPInstructionType.put( "bitwXor", CPType.Binary); - String2CPInstructionType.put( "bitwShiftL", CPType.Binary); - String2CPInstructionType.put( "bitwShiftR", CPType.Binary); - String2CPInstructionType.put( "!" , CPType.Unary); - - // Relational Instruction Opcodes - String2CPInstructionType.put( "==" , CPType.Binary); - String2CPInstructionType.put( "!=" , CPType.Binary); - String2CPInstructionType.put( "<" , CPType.Binary); - String2CPInstructionType.put( ">" , CPType.Binary); - String2CPInstructionType.put( "<=" , CPType.Binary); - String2CPInstructionType.put( ">=" , CPType.Binary); - - // Builtin Instruction Opcodes - String2CPInstructionType.put( "log" , CPType.Builtin); - String2CPInstructionType.put( "log_nz" , CPType.Builtin); - - String2CPInstructionType.put( "solve" , CPType.Binary); - String2CPInstructionType.put( "max" , CPType.Binary); - String2CPInstructionType.put( "min" , CPType.Binary); - String2CPInstructionType.put( "dropInvalidType" , CPType.Binary); - String2CPInstructionType.put( "dropInvalidLength" , CPType.Binary); - String2CPInstructionType.put( "freplicate" , CPType.Binary); - String2CPInstructionType.put( "valueSwap" , CPType.Binary); - String2CPInstructionType.put( "applySchema" , CPType.Binary); - String2CPInstructionType.put( "_map" , CPType.Ternary); // _map represents the operation map - - String2CPInstructionType.put( "nmax", CPType.BuiltinNary); - String2CPInstructionType.put( "nmin", CPType.BuiltinNary); - String2CPInstructionType.put( "n+" , CPType.BuiltinNary); - - String2CPInstructionType.put( "exp" , CPType.Unary); - String2CPInstructionType.put( "abs" , CPType.Unary); - String2CPInstructionType.put( "sin" , CPType.Unary); - String2CPInstructionType.put( "cos" , CPType.Unary); - String2CPInstructionType.put( "tan" , CPType.Unary); - String2CPInstructionType.put( "sinh" , CPType.Unary); - String2CPInstructionType.put( "cosh" , CPType.Unary); - String2CPInstructionType.put( "tanh" , CPType.Unary); - String2CPInstructionType.put( "asin" , CPType.Unary); - String2CPInstructionType.put( "acos" , CPType.Unary); - String2CPInstructionType.put( "atan" , CPType.Unary); - String2CPInstructionType.put( "sign" , CPType.Unary); - String2CPInstructionType.put( "sqrt" , CPType.Unary); - String2CPInstructionType.put( "plogp" , CPType.Unary); - String2CPInstructionType.put( "print" , CPType.Unary); - String2CPInstructionType.put( "assert" , CPType.Unary); - String2CPInstructionType.put( "round" , CPType.Unary); - String2CPInstructionType.put( "ceil" , CPType.Unary); - String2CPInstructionType.put( "floor" , CPType.Unary); - String2CPInstructionType.put( "ucumk+", CPType.Unary); - String2CPInstructionType.put( "ucum*" , CPType.Unary); - String2CPInstructionType.put( "ucumk+*" , CPType.Unary); - String2CPInstructionType.put( "ucummin", CPType.Unary); - String2CPInstructionType.put( "ucummax", CPType.Unary); - String2CPInstructionType.put( "stop" , CPType.Unary); - String2CPInstructionType.put( "inverse", CPType.Unary); - String2CPInstructionType.put( "cholesky",CPType.Unary); - String2CPInstructionType.put( "sprop", CPType.Unary); - String2CPInstructionType.put( "sigmoid", CPType.Unary); - String2CPInstructionType.put( "typeOf", CPType.Unary); - String2CPInstructionType.put( "detectSchema", CPType.Unary); - String2CPInstructionType.put( "colnames", CPType.Unary); - String2CPInstructionType.put( "isna", CPType.Unary); - String2CPInstructionType.put( "isnan", CPType.Unary); - String2CPInstructionType.put( "isinf", CPType.Unary); - String2CPInstructionType.put( "printf", CPType.BuiltinNary); - String2CPInstructionType.put( "cbind", CPType.BuiltinNary); - String2CPInstructionType.put( "rbind", CPType.BuiltinNary); - String2CPInstructionType.put( "eval", CPType.BuiltinNary); - String2CPInstructionType.put( "list", CPType.BuiltinNary); - + String2CPInstructionType.put("ba+*", CPType.AggregateBinary); + String2CPInstructionType.put("tak+*", CPType.AggregateTernary); + String2CPInstructionType.put("tack+*", CPType.AggregateTernary); + + String2CPInstructionType.put("uak+", CPType.AggregateUnary); + String2CPInstructionType.put("uark+", CPType.AggregateUnary); + String2CPInstructionType.put("uack+", CPType.AggregateUnary); + String2CPInstructionType.put("uasqk+", CPType.AggregateUnary); + String2CPInstructionType.put("uarsqk+", CPType.AggregateUnary); + String2CPInstructionType.put("uacsqk+", CPType.AggregateUnary); + String2CPInstructionType.put("uamean", CPType.AggregateUnary); + String2CPInstructionType.put("uarmean", CPType.AggregateUnary); + String2CPInstructionType.put("uacmean", CPType.AggregateUnary); + String2CPInstructionType.put("uavar", CPType.AggregateUnary); + String2CPInstructionType.put("uarvar", CPType.AggregateUnary); + String2CPInstructionType.put("uacvar", CPType.AggregateUnary); + String2CPInstructionType.put("uamax", CPType.AggregateUnary); + String2CPInstructionType.put("uarmax", CPType.AggregateUnary); + String2CPInstructionType.put("uarimax", CPType.AggregateUnary); + String2CPInstructionType.put("uacmax", CPType.AggregateUnary); + String2CPInstructionType.put("uamin", CPType.AggregateUnary); + String2CPInstructionType.put("uarmin", CPType.AggregateUnary); + String2CPInstructionType.put("uarimin", CPType.AggregateUnary); + String2CPInstructionType.put("uacmin", CPType.AggregateUnary); + String2CPInstructionType.put("ua+", CPType.AggregateUnary); + String2CPInstructionType.put("uar+", CPType.AggregateUnary); + String2CPInstructionType.put("uac+", CPType.AggregateUnary); + String2CPInstructionType.put("ua*", CPType.AggregateUnary); + String2CPInstructionType.put("uar*", CPType.AggregateUnary); + String2CPInstructionType.put("uac*", CPType.AggregateUnary); + String2CPInstructionType.put("uatrace", CPType.AggregateUnary); + String2CPInstructionType.put("uaktrace", CPType.AggregateUnary); + String2CPInstructionType.put("nrow", CPType.AggregateUnary); + String2CPInstructionType.put("ncol", CPType.AggregateUnary); + String2CPInstructionType.put("length", CPType.AggregateUnary); + String2CPInstructionType.put("exists", CPType.AggregateUnary); + String2CPInstructionType.put("lineage", CPType.AggregateUnary); + String2CPInstructionType.put("uacd", CPType.AggregateUnary); + String2CPInstructionType.put("uacdr", CPType.AggregateUnary); + String2CPInstructionType.put("uacdc", CPType.AggregateUnary); + String2CPInstructionType.put("uacdap", CPType.AggregateUnary); + String2CPInstructionType.put("uacdapr", CPType.AggregateUnary); + String2CPInstructionType.put("uacdapc", CPType.AggregateUnary); + String2CPInstructionType.put("unique", CPType.AggregateUnary); + String2CPInstructionType.put("uniquer", CPType.AggregateUnary); + String2CPInstructionType.put("uniquec", CPType.AggregateUnary); + + String2CPInstructionType.put("uaggouterchain", CPType.UaggOuterChain); + + // Arithmetic Instruction Opcodes + String2CPInstructionType.put("+", CPType.Binary); + String2CPInstructionType.put("-", CPType.Binary); + String2CPInstructionType.put("*", CPType.Binary); + String2CPInstructionType.put("/", CPType.Binary); + String2CPInstructionType.put("%%", CPType.Binary); + String2CPInstructionType.put("%/%", CPType.Binary); + String2CPInstructionType.put("^", CPType.Binary); + String2CPInstructionType.put("1-*", CPType.Binary); // special * case + String2CPInstructionType.put("^2", CPType.Binary); // special ^ case + String2CPInstructionType.put("*2", CPType.Binary); // special * case + String2CPInstructionType.put("-nz", CPType.Binary); // special - case + + // Boolean Instruction Opcodes + String2CPInstructionType.put("&&", CPType.Binary); + String2CPInstructionType.put("||", CPType.Binary); + String2CPInstructionType.put("xor", CPType.Binary); + String2CPInstructionType.put("bitwAnd", CPType.Binary); + String2CPInstructionType.put("bitwOr", CPType.Binary); + String2CPInstructionType.put("bitwXor", CPType.Binary); + String2CPInstructionType.put("bitwShiftL", CPType.Binary); + String2CPInstructionType.put("bitwShiftR", CPType.Binary); + String2CPInstructionType.put("!", CPType.Unary); + + // Relational Instruction Opcodes + String2CPInstructionType.put("==", CPType.Binary); + String2CPInstructionType.put("!=", CPType.Binary); + String2CPInstructionType.put("<", CPType.Binary); + String2CPInstructionType.put(">", CPType.Binary); + String2CPInstructionType.put("<=", CPType.Binary); + String2CPInstructionType.put(">=", CPType.Binary); + + // Builtin Instruction Opcodes + String2CPInstructionType.put("log", CPType.Builtin); + String2CPInstructionType.put("log_nz", CPType.Builtin); + + String2CPInstructionType.put("solve", CPType.Binary); + String2CPInstructionType.put("max", CPType.Binary); + String2CPInstructionType.put("min", CPType.Binary); + String2CPInstructionType.put("dropInvalidType", CPType.Binary); + String2CPInstructionType.put("dropInvalidLength", CPType.Binary); + String2CPInstructionType.put("freplicate", CPType.Binary); + String2CPInstructionType.put("valueSwap", CPType.Binary); + String2CPInstructionType.put("applySchema", CPType.Binary); + String2CPInstructionType.put("_map", CPType.Ternary); // _map represents the operation map + + String2CPInstructionType.put("nmax", CPType.BuiltinNary); + String2CPInstructionType.put("nmin", CPType.BuiltinNary); + String2CPInstructionType.put("n+", CPType.BuiltinNary); + + String2CPInstructionType.put("exp", CPType.Unary); + String2CPInstructionType.put("abs", CPType.Unary); + String2CPInstructionType.put("sin", CPType.Unary); + String2CPInstructionType.put("cos", CPType.Unary); + String2CPInstructionType.put("tan", CPType.Unary); + String2CPInstructionType.put("sinh", CPType.Unary); + String2CPInstructionType.put("cosh", CPType.Unary); + String2CPInstructionType.put("tanh", CPType.Unary); + String2CPInstructionType.put("asin", CPType.Unary); + String2CPInstructionType.put("acos", CPType.Unary); + String2CPInstructionType.put("atan", CPType.Unary); + String2CPInstructionType.put("sign", CPType.Unary); + String2CPInstructionType.put("sqrt", CPType.Unary); + String2CPInstructionType.put("plogp", CPType.Unary); + String2CPInstructionType.put("print", CPType.Unary); + String2CPInstructionType.put("assert", CPType.Unary); + String2CPInstructionType.put("round", CPType.Unary); + String2CPInstructionType.put("ceil", CPType.Unary); + String2CPInstructionType.put("floor", CPType.Unary); + String2CPInstructionType.put("ucumk+", CPType.Unary); + String2CPInstructionType.put("ucum*", CPType.Unary); + String2CPInstructionType.put("ucumk+*", CPType.Unary); + String2CPInstructionType.put("ucummin", CPType.Unary); + String2CPInstructionType.put("ucummax", CPType.Unary); + String2CPInstructionType.put("stop", CPType.Unary); + String2CPInstructionType.put("inverse", CPType.Unary); + String2CPInstructionType.put("cholesky", CPType.Unary); + String2CPInstructionType.put("sprop", CPType.Unary); + String2CPInstructionType.put("sigmoid", CPType.Unary); + String2CPInstructionType.put("typeOf", CPType.Unary); + String2CPInstructionType.put("detectSchema", CPType.Unary); + String2CPInstructionType.put("colnames", CPType.Unary); + String2CPInstructionType.put("isna", CPType.Unary); + String2CPInstructionType.put("isnan", CPType.Unary); + String2CPInstructionType.put("isinf", CPType.Unary); + String2CPInstructionType.put("printf", CPType.BuiltinNary); + String2CPInstructionType.put("cbind", CPType.BuiltinNary); + String2CPInstructionType.put("rbind", CPType.BuiltinNary); + String2CPInstructionType.put("eval", CPType.BuiltinNary); + String2CPInstructionType.put("list", CPType.BuiltinNary); + // Parameterized Builtin Functions - String2CPInstructionType.put( "autoDiff" , CPType.ParameterizedBuiltin); - String2CPInstructionType.put( "contains", CPType.ParameterizedBuiltin); - String2CPInstructionType.put("paramserv", CPType.ParameterizedBuiltin); - String2CPInstructionType.put( "nvlist", CPType.ParameterizedBuiltin); - String2CPInstructionType.put( "cdf", CPType.ParameterizedBuiltin); - String2CPInstructionType.put( "invcdf", CPType.ParameterizedBuiltin); - String2CPInstructionType.put( "groupedagg", CPType.ParameterizedBuiltin); - String2CPInstructionType.put( "rmempty" , CPType.ParameterizedBuiltin); - String2CPInstructionType.put( "replace", CPType.ParameterizedBuiltin); - String2CPInstructionType.put( "lowertri", CPType.ParameterizedBuiltin); - String2CPInstructionType.put( "uppertri", CPType.ParameterizedBuiltin); - String2CPInstructionType.put( "rexpand", CPType.ParameterizedBuiltin); - String2CPInstructionType.put( "toString", CPType.ParameterizedBuiltin); - String2CPInstructionType.put( "tokenize", CPType.ParameterizedBuiltin); - String2CPInstructionType.put( "transformapply", CPType.ParameterizedBuiltin); - String2CPInstructionType.put( "transformdecode",CPType.ParameterizedBuiltin); - String2CPInstructionType.put( "transformcolmap",CPType.ParameterizedBuiltin); - String2CPInstructionType.put( "transformmeta", CPType.ParameterizedBuiltin); - String2CPInstructionType.put( "transformencode",CPType.MultiReturnParameterizedBuiltin); - + String2CPInstructionType.put("autoDiff", CPType.ParameterizedBuiltin); + String2CPInstructionType.put("contains", CPType.ParameterizedBuiltin); + String2CPInstructionType.put("paramserv", CPType.ParameterizedBuiltin); + String2CPInstructionType.put("nvlist", CPType.ParameterizedBuiltin); + String2CPInstructionType.put("cdf", CPType.ParameterizedBuiltin); + String2CPInstructionType.put("invcdf", CPType.ParameterizedBuiltin); + String2CPInstructionType.put("groupedagg", CPType.ParameterizedBuiltin); + String2CPInstructionType.put("rmempty", CPType.ParameterizedBuiltin); + String2CPInstructionType.put("replace", CPType.ParameterizedBuiltin); + String2CPInstructionType.put("lowertri", CPType.ParameterizedBuiltin); + String2CPInstructionType.put("uppertri", CPType.ParameterizedBuiltin); + String2CPInstructionType.put("rexpand", CPType.ParameterizedBuiltin); + String2CPInstructionType.put("toString", CPType.ParameterizedBuiltin); + String2CPInstructionType.put("tokenize", CPType.ParameterizedBuiltin); + String2CPInstructionType.put("transformapply", CPType.ParameterizedBuiltin); + String2CPInstructionType.put("transformdecode", CPType.ParameterizedBuiltin); + String2CPInstructionType.put("transformcolmap", CPType.ParameterizedBuiltin); + String2CPInstructionType.put("transformmeta", CPType.ParameterizedBuiltin); + String2CPInstructionType.put("transformencode", CPType.MultiReturnParameterizedBuiltin); + // Ternary Instruction Opcodes - String2CPInstructionType.put( "+*", CPType.Ternary); - String2CPInstructionType.put( "-*", CPType.Ternary); - String2CPInstructionType.put( "ifelse", CPType.Ternary); - - // Variable Instruction Opcodes - String2CPInstructionType.put( "assignvar" , CPType.Variable); - String2CPInstructionType.put( "cpvar" , CPType.Variable); - String2CPInstructionType.put( "mvvar" , CPType.Variable); - String2CPInstructionType.put( "rmvar" , CPType.Variable); - String2CPInstructionType.put( "rmfilevar" , CPType.Variable); - String2CPInstructionType.put( OpOp1.CAST_AS_SCALAR.toString(), CPType.Variable); - String2CPInstructionType.put( OpOp1.CAST_AS_MATRIX.toString(), CPType.Variable); - String2CPInstructionType.put( "cast_as_frame", CPType.Variable); - String2CPInstructionType.put( OpOp1.CAST_AS_FRAME.toString(), CPType.Variable); - String2CPInstructionType.put( OpOp1.CAST_AS_LIST.toString(), CPType.Variable); - String2CPInstructionType.put( OpOp1.CAST_AS_DOUBLE.toString(), CPType.Variable); - String2CPInstructionType.put( OpOp1.CAST_AS_INT.toString(), CPType.Variable); - String2CPInstructionType.put( OpOp1.CAST_AS_BOOLEAN.toString(), CPType.Variable); - String2CPInstructionType.put( "attachfiletovar" , CPType.Variable); - String2CPInstructionType.put( "read" , CPType.Variable); - String2CPInstructionType.put( "write" , CPType.Variable); - String2CPInstructionType.put( "createvar" , CPType.Variable); + String2CPInstructionType.put("+*", CPType.Ternary); + String2CPInstructionType.put("-*", CPType.Ternary); + String2CPInstructionType.put("ifelse", CPType.Ternary); + + // Variable Instruction Opcodes + String2CPInstructionType.put("assignvar", CPType.Variable); + String2CPInstructionType.put("cpvar", CPType.Variable); + String2CPInstructionType.put("mvvar", CPType.Variable); + String2CPInstructionType.put("rmvar", CPType.Variable); + String2CPInstructionType.put("rmfilevar", CPType.Variable); + String2CPInstructionType.put(OpOp1.CAST_AS_SCALAR.toString(), CPType.Variable); + String2CPInstructionType.put(OpOp1.CAST_AS_MATRIX.toString(), CPType.Variable); + String2CPInstructionType.put("cast_as_frame", CPType.Variable); + String2CPInstructionType.put(OpOp1.CAST_AS_FRAME.toString(), CPType.Variable); + String2CPInstructionType.put(OpOp1.CAST_AS_LIST.toString(), CPType.Variable); + String2CPInstructionType.put(OpOp1.CAST_AS_DOUBLE.toString(), CPType.Variable); + String2CPInstructionType.put(OpOp1.CAST_AS_INT.toString(), CPType.Variable); + String2CPInstructionType.put(OpOp1.CAST_AS_BOOLEAN.toString(), CPType.Variable); + String2CPInstructionType.put("attachfiletovar", CPType.Variable); + String2CPInstructionType.put("read", CPType.Variable); + String2CPInstructionType.put("write", CPType.Variable); + String2CPInstructionType.put("createvar", CPType.Variable); // Reorg Instruction Opcodes (repositioning of existing values) - String2CPInstructionType.put( "r'" , CPType.Reorg); - String2CPInstructionType.put( "rev" , CPType.Reorg); - String2CPInstructionType.put( "rdiag" , CPType.Reorg); - String2CPInstructionType.put( "rshape" , CPType.Reshape); - String2CPInstructionType.put( "rsort" , CPType.Reorg); + String2CPInstructionType.put("r'", CPType.Reorg); + String2CPInstructionType.put("rev", CPType.Reorg); + String2CPInstructionType.put("rdiag", CPType.Reorg); + String2CPInstructionType.put("rshape", CPType.Reshape); + String2CPInstructionType.put("rsort", CPType.Reorg); // Opcodes related to convolutions - String2CPInstructionType.put( "relu_backward" , CPType.Dnn); - String2CPInstructionType.put( "relu_maxpooling" , CPType.Dnn); - String2CPInstructionType.put( "relu_maxpooling_backward" , CPType.Dnn); - String2CPInstructionType.put( "maxpooling" , CPType.Dnn); - String2CPInstructionType.put( "maxpooling_backward" , CPType.Dnn); - String2CPInstructionType.put( "avgpooling" , CPType.Dnn); - String2CPInstructionType.put( "avgpooling_backward" , CPType.Dnn); - String2CPInstructionType.put( "conv2d" , CPType.Dnn); - String2CPInstructionType.put( "conv2d_bias_add" , CPType.Dnn); - String2CPInstructionType.put( "conv2d_backward_filter" , CPType.Dnn); - String2CPInstructionType.put( "conv2d_backward_data" , CPType.Dnn); - String2CPInstructionType.put( "bias_add" , CPType.Dnn); - String2CPInstructionType.put( "bias_multiply" , CPType.Dnn); - String2CPInstructionType.put( "batch_norm2d", CPType.Dnn); - String2CPInstructionType.put( "batch_norm2d_backward", CPType.Dnn); - + String2CPInstructionType.put("relu_backward", CPType.Dnn); + String2CPInstructionType.put("relu_maxpooling", CPType.Dnn); + String2CPInstructionType.put("relu_maxpooling_backward", CPType.Dnn); + String2CPInstructionType.put("maxpooling", CPType.Dnn); + String2CPInstructionType.put("maxpooling_backward", CPType.Dnn); + String2CPInstructionType.put("avgpooling", CPType.Dnn); + String2CPInstructionType.put("avgpooling_backward", CPType.Dnn); + String2CPInstructionType.put("conv2d", CPType.Dnn); + String2CPInstructionType.put("conv2d_bias_add", CPType.Dnn); + String2CPInstructionType.put("conv2d_backward_filter", CPType.Dnn); + String2CPInstructionType.put("conv2d_backward_data", CPType.Dnn); + String2CPInstructionType.put("bias_add", CPType.Dnn); + String2CPInstructionType.put("bias_multiply", CPType.Dnn); + String2CPInstructionType.put("batch_norm2d", CPType.Dnn); + String2CPInstructionType.put("batch_norm2d_backward", CPType.Dnn); + // Quaternary instruction opcodes - String2CPInstructionType.put( "wsloss" , CPType.Quaternary); - String2CPInstructionType.put( "wsigmoid", CPType.Quaternary); - String2CPInstructionType.put( "wdivmm", CPType.Quaternary); - String2CPInstructionType.put( "wcemm", CPType.Quaternary); - String2CPInstructionType.put( "wumm", CPType.Quaternary); - + String2CPInstructionType.put("wsloss", CPType.Quaternary); + String2CPInstructionType.put("wsigmoid", CPType.Quaternary); + String2CPInstructionType.put("wdivmm", CPType.Quaternary); + String2CPInstructionType.put("wcemm", CPType.Quaternary); + String2CPInstructionType.put("wumm", CPType.Quaternary); + // User-defined function Opcodes String2CPInstructionType.put(FunctionOp.OPCODE, CPType.FCall); String2CPInstructionType.put(Append.OPCODE, CPType.Append); - String2CPInstructionType.put( "remove", CPType.Append); - + String2CPInstructionType.put("remove", CPType.Append); + // data generation opcodes - String2CPInstructionType.put( DataGen.RAND_OPCODE , CPType.Rand); - String2CPInstructionType.put( DataGen.SEQ_OPCODE , CPType.Rand); - String2CPInstructionType.put( DataGen.SINIT_OPCODE , CPType.StringInit); - String2CPInstructionType.put( DataGen.SAMPLE_OPCODE , CPType.Rand); - String2CPInstructionType.put( DataGen.TIME_OPCODE , CPType.Rand); - String2CPInstructionType.put( DataGen.FRAME_OPCODE , CPType.Rand); - - String2CPInstructionType.put( "ctable", CPType.Ctable); - String2CPInstructionType.put( "ctableexpand", CPType.Ctable); - - //central moment, covariance, quantiles (sort/pick) - String2CPInstructionType.put( "cm", CPType.CentralMoment); - String2CPInstructionType.put( "cov", CPType.Covariance); - String2CPInstructionType.put( "qsort", CPType.QSort); - String2CPInstructionType.put( "qpick", CPType.QPick); - - - String2CPInstructionType.put( RightIndex.OPCODE, CPType.MatrixIndexing); - String2CPInstructionType.put( LeftIndex.OPCODE, CPType.MatrixIndexing); - - String2CPInstructionType.put( "tsmm", CPType.MMTSJ); - String2CPInstructionType.put( "pmm", CPType.PMMJ); - String2CPInstructionType.put( "mmchain", CPType.MMChain); - - String2CPInstructionType.put( "qr", CPType.MultiReturnBuiltin); - String2CPInstructionType.put( "lu", CPType.MultiReturnBuiltin); - String2CPInstructionType.put( "eigen", CPType.MultiReturnBuiltin); - String2CPInstructionType.put( "fft", CPType.MultiReturnBuiltin); - String2CPInstructionType.put( "ifft", CPType.MultiReturnBuiltin); - String2CPInstructionType.put( "svd", CPType.MultiReturnBuiltin); - - String2CPInstructionType.put( "partition", CPType.Partition); - String2CPInstructionType.put( Compression.OPCODE, CPType.Compression); - String2CPInstructionType.put( DeCompression.OPCODE, CPType.DeCompression); - String2CPInstructionType.put( "spoof", CPType.SpoofFused); - String2CPInstructionType.put( "prefetch", CPType.Prefetch); - String2CPInstructionType.put( "broadcast", CPType.Broadcast); - String2CPInstructionType.put( "trigremote", CPType.TrigRemote); - String2CPInstructionType.put( Local.OPCODE, CPType.Local); - - String2CPInstructionType.put( "sql", CPType.Sql); + String2CPInstructionType.put(DataGen.RAND_OPCODE, CPType.Rand); + String2CPInstructionType.put(DataGen.SEQ_OPCODE, CPType.Rand); + String2CPInstructionType.put(DataGen.SINIT_OPCODE, CPType.StringInit); + String2CPInstructionType.put(DataGen.SAMPLE_OPCODE, CPType.Rand); + String2CPInstructionType.put(DataGen.TIME_OPCODE, CPType.Rand); + String2CPInstructionType.put(DataGen.FRAME_OPCODE, CPType.Rand); + + String2CPInstructionType.put("ctable", CPType.Ctable); + String2CPInstructionType.put("ctableexpand", CPType.Ctable); + + // central moment, covariance, quantiles (sort/pick) + String2CPInstructionType.put("cm", CPType.CentralMoment); + String2CPInstructionType.put("cov", CPType.Covariance); + String2CPInstructionType.put("qsort", CPType.QSort); + String2CPInstructionType.put("qpick", CPType.QPick); + + String2CPInstructionType.put(RightIndex.OPCODE, CPType.MatrixIndexing); + String2CPInstructionType.put(LeftIndex.OPCODE, CPType.MatrixIndexing); + + String2CPInstructionType.put("tsmm", CPType.MMTSJ); + String2CPInstructionType.put("pmm", CPType.PMMJ); + String2CPInstructionType.put("mmchain", CPType.MMChain); + + String2CPInstructionType.put("qr", CPType.MultiReturnBuiltin); + String2CPInstructionType.put("lu", CPType.MultiReturnBuiltin); + String2CPInstructionType.put("eigen", CPType.MultiReturnBuiltin); + String2CPInstructionType.put("fft", CPType.MultiReturnBuiltin); + String2CPInstructionType.put("ifft", CPType.MultiReturnMatrixMatrixBuiltin); + String2CPInstructionType.put("svd", CPType.MultiReturnBuiltin); + + String2CPInstructionType.put("partition", CPType.Partition); + String2CPInstructionType.put(Compression.OPCODE, CPType.Compression); + String2CPInstructionType.put(DeCompression.OPCODE, CPType.DeCompression); + String2CPInstructionType.put("spoof", CPType.SpoofFused); + String2CPInstructionType.put("prefetch", CPType.Prefetch); + String2CPInstructionType.put("broadcast", CPType.Broadcast); + String2CPInstructionType.put("trigremote", CPType.TrigRemote); + String2CPInstructionType.put(Local.OPCODE, CPType.Local); + + String2CPInstructionType.put("sql", CPType.Sql); } - public static CPInstruction parseSingleInstruction (String str ) { - if ( str == null || str.isEmpty() ) + public static CPInstruction parseSingleInstruction(String str) { + if (str == null || str.isEmpty()) return null; - CPType cptype = InstructionUtils.getCPType(str); - if ( cptype == null ) + CPType cptype = InstructionUtils.getCPType(str); + if (cptype == null) throw new DMLRuntimeException("Unable derive cptype for instruction: " + str); CPInstruction cpinst = parseSingleInstruction(cptype, str); - if ( cpinst == null ) + if (cpinst == null) throw new DMLRuntimeException("Unable to parse instruction: " + str); return cpinst; } - - public static CPInstruction parseSingleInstruction ( CPType cptype, String str ) { + + public static CPInstruction parseSingleInstruction(CPType cptype, String str) { ExecType execType; - if ( str == null || str.isEmpty() ) + if (str == null || str.isEmpty()) return null; - switch(cptype) { + switch (cptype) { case AggregateUnary: return AggregateUnaryCPInstruction.parseInstruction(str); - + case AggregateBinary: return AggregateBinaryCPInstruction.parseInstruction(str); - + case AggregateTernary: return AggregateTernaryCPInstruction.parseInstruction(str); - + case Unary: return UnaryCPInstruction.parseInstruction(str); case Binary: return BinaryCPInstruction.parseInstruction(str); - + case Ternary: return TernaryCPInstruction.parseInstruction(str); - + case Quaternary: return QuaternaryCPInstruction.parseInstruction(str); - + case BuiltinNary: return BuiltinNaryCPInstruction.parseInstruction(str); - + case Ctable: return CtableCPInstruction.parseInstruction(str); - + case Reorg: return ReorgCPInstruction.parseInstruction(str); - + case Dnn: - return DnnCPInstruction.parseInstruction(str); - + return DnnCPInstruction.parseInstruction(str); + case UaggOuterChain: return UaggOuterChainCPInstruction.parseInstruction(str); - + case Reshape: return ReshapeCPInstruction.parseInstruction(str); - + case Append: return AppendCPInstruction.parseInstruction(str); - + case Variable: return VariableCPInstruction.parseInstruction(str); - + case Rand: return DataGenCPInstruction.parseInstruction(str); case StringInit: return StringInitCPInstruction.parseInstruction(str); - + case FCall: return FunctionCallCPInstruction.parseInstruction(str); case ParameterizedBuiltin: return ParameterizedBuiltinCPInstruction.parseInstruction(str); - + case MultiReturnParameterizedBuiltin: return MultiReturnParameterizedBuiltinCPInstruction.parseInstruction(str); - + + case MultiReturnMatrixMatrixBuiltin: + return MultiReturnMatrixMatrixBuiltinCPInstruction.parseInstruction(str); + case MultiReturnBuiltin: return MultiReturnBuiltinCPInstruction.parseInstruction(str); - + case QSort: return QuantileSortCPInstruction.parseInstruction(str); - + case QPick: return QuantilePickCPInstruction.parseInstruction(str); - + case MatrixIndexing: - execType = ExecType.valueOf( str.split(Instruction.OPERAND_DELIM)[0] ); - if( execType == ExecType.CP ) + execType = ExecType.valueOf(str.split(Instruction.OPERAND_DELIM)[0]); + if (execType == ExecType.CP) return IndexingCPInstruction.parseInstruction(str); - else //exectype CP_FILE + else // exectype CP_FILE return MatrixIndexingCPFileInstruction.parseInstruction(str); - - case Builtin: + + case Builtin: String[] parts = InstructionUtils.getInstructionPartsWithValueType(str); - if(parts[0].equals("log") || parts[0].equals("log_nz")) { - if(InstructionUtils.isInteger(parts[3])) // B=log(A), y=log(x) - // We exploit the fact the number of threads is specified as an integer at parts 3. + if (parts[0].equals("log") || parts[0].equals("log_nz")) { + if (InstructionUtils.isInteger(parts[3])) // B=log(A), y=log(x) + // We exploit the fact the number of threads is specified as an integer at parts + // 3. return UnaryCPInstruction.parseInstruction(str); else // B=log(A,10), y=log(x,10) return BinaryCPInstruction.parseInstruction(str); } - throw new DMLRuntimeException("Invalid Builtin Instruction: " + str ); - + throw new DMLRuntimeException("Invalid Builtin Instruction: " + str); + case MMTSJ: return MMTSJCPInstruction.parseInstruction(str); - + case PMMJ: return PMMJCPInstruction.parseInstruction(str); - + case MMChain: return MMChainCPInstruction.parseInstruction(str); - + case CentralMoment: return CentralMomentCPInstruction.parseInstruction(str); - + case Covariance: return CovarianceCPInstruction.parseInstruction(str); case Compression: return CompressionCPInstruction.parseInstruction(str); - + case DeCompression: return DeCompressionCPInstruction.parseInstruction(str); - + case Local: return LocalCPInstruction.parseInstruction(str); case SpoofFused: return SpoofCPInstruction.parseInstruction(str); - + case Sql: return SqlCPInstruction.parseInstruction(str); - + case Prefetch: return PrefetchCPInstruction.parseInstruction(str); - + case Broadcast: return BroadcastCPInstruction.parseInstruction(str); - + default: - throw new DMLRuntimeException("Invalid CP Instruction Type: " + cptype ); + throw new DMLRuntimeException("Invalid CP Instruction Type: " + cptype); } } } diff --git a/src/main/java/org/apache/sysds/runtime/instructions/cp/CPInstruction.java b/src/main/java/org/apache/sysds/runtime/instructions/cp/CPInstruction.java index 3503b256f77..434650d18a4 100644 --- a/src/main/java/org/apache/sysds/runtime/instructions/cp/CPInstruction.java +++ b/src/main/java/org/apache/sysds/runtime/instructions/cp/CPInstruction.java @@ -19,7 +19,6 @@ package org.apache.sysds.runtime.instructions.cp; - import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.sysds.api.DMLScript; @@ -38,15 +37,16 @@ public abstract class CPInstruction extends Instruction { protected static final Log LOG = LogFactory.getLog(CPInstruction.class.getName()); + public enum CPType { AggregateUnary, AggregateBinary, AggregateTernary, Unary, Binary, Ternary, Quaternary, BuiltinNary, Ctable, - MultiReturnParameterizedBuiltin, ParameterizedBuiltin, MultiReturnBuiltin, + MultiReturnParameterizedBuiltin, ParameterizedBuiltin, MultiReturnBuiltin, MultiReturnMatrixMatrixBuiltin, Builtin, Reorg, Variable, FCall, Append, Rand, QSort, QPick, Local, MatrixIndexing, MMTSJ, PMMJ, MMChain, Reshape, Partition, Compression, DeCompression, SpoofFused, StringInit, CentralMoment, Covariance, UaggOuterChain, Dnn, Sql, Prefetch, Broadcast, TrigRemote, NoOp, - } + } protected final CPType _cptype; protected final boolean _requiresLabelUpdate; @@ -64,7 +64,7 @@ protected CPInstruction(CPType type, Operator op, String opcode, String istr) { instOpcode = opcode; _requiresLabelUpdate = super.requiresLabelUpdate(); } - + @Override public IType getType() { return IType.CONTROL_PROGRAM; @@ -73,7 +73,7 @@ public IType getType() { public CPType getCPInstructionType() { return _cptype; } - + @Override public boolean requiresLabelUpdate() { return _requiresLabelUpdate; @@ -86,31 +86,32 @@ public String getGraphString() { @Override public Instruction preprocessInstruction(ExecutionContext ec) { - //default preprocess behavior (e.g., debug state, lineage) + // default preprocess behavior (e.g., debug state, lineage) Instruction tmp = super.preprocessInstruction(ec); - //instruction patching - if( tmp.requiresLabelUpdate() ) { //update labels only if required - //note: no exchange of updated instruction as labels might change in the general case + // instruction patching + if (tmp.requiresLabelUpdate()) { // update labels only if required + // note: no exchange of updated instruction as labels might change in the + // general case String updInst = updateLabels(tmp.toString(), ec.getVariables()); tmp = CPInstructionParser.parseSingleInstruction(updInst); // Corrected lineage trace for patched instructions if (DMLScript.LINEAGE) ec.traceLineage(tmp); } - - //robustness federated instructions (runtime assignment) - if( ConfigurationManager.isFederatedRuntimePlanner() ) { + + // robustness federated instructions (runtime assignment) + if (ConfigurationManager.isFederatedRuntimePlanner()) { tmp = FEDInstructionUtils.checkAndReplaceCP(tmp, ec); - //NOTE: Retracing of lineage is not needed as the lineage trace - //is same for an instruction and its FED version. + // NOTE: Retracing of lineage is not needed as the lineage trace + // is same for an instruction and its FED version. } - + tmp = PrivacyPropagator.preprocessInstruction(tmp, ec); return tmp; } - @Override + @Override public abstract void processInstruction(ExecutionContext ec); @Override @@ -118,60 +119,63 @@ public void postprocessInstruction(ExecutionContext ec) { if (DMLScript.LINEAGE_DEBUGGER) ec.maintainLineageDebuggerInfo(this); } - + /** - * Takes a delimited string of instructions, and replaces ALL placeholder labels + * Takes a delimited string of instructions, and replaces ALL placeholder labels * (such as ##mVar2## and ##Var5##) in ALL instructions. - * - * @param instList instruction list as string + * + * @param instList instruction list as string * @param labelValueMapping local variable map * @return instruction list after replacement */ - public static String updateLabels (String instList, LocalVariableMap labelValueMapping) { + public static String updateLabels(String instList, LocalVariableMap labelValueMapping) { - if ( !instList.contains(Lop.VARIABLE_NAME_PLACEHOLDER) ) + if (!instList.contains(Lop.VARIABLE_NAME_PLACEHOLDER)) return instList; - + StringBuilder updateInstList = new StringBuilder(); - String[] ilist = instList.split(Lop.INSTRUCTION_DELIMITOR); - - for ( int i=0; i < ilist.length; i++ ) { - if ( i > 0 ) + String[] ilist = instList.split(Lop.INSTRUCTION_DELIMITOR); + + for (int i = 0; i < ilist.length; i++) { + if (i > 0) updateInstList.append(Lop.INSTRUCTION_DELIMITOR); - - updateInstList.append( updateInstLabels(ilist[i], labelValueMapping)); + + updateInstList.append(updateInstLabels(ilist[i], labelValueMapping)); } return updateInstList.toString(); } - /** - * Replaces ALL placeholder strings (such as ##mVar2## and ##Var5##) in a single instruction. - * + /** + * Replaces ALL placeholder strings (such as ##mVar2## and ##Var5##) in a single + * instruction. + * * @param inst string instruction - * @param map local variable map + * @param map local variable map * @return string instruction after replacement */ private static String updateInstLabels(String inst, LocalVariableMap map) { - if ( inst.contains(Lop.VARIABLE_NAME_PLACEHOLDER) ) { + if (inst.contains(Lop.VARIABLE_NAME_PLACEHOLDER)) { int skip = Lop.VARIABLE_NAME_PLACEHOLDER.length(); - while ( inst.contains(Lop.VARIABLE_NAME_PLACEHOLDER) ) { - int startLoc = inst.indexOf(Lop.VARIABLE_NAME_PLACEHOLDER)+skip; + while (inst.contains(Lop.VARIABLE_NAME_PLACEHOLDER)) { + int startLoc = inst.indexOf(Lop.VARIABLE_NAME_PLACEHOLDER) + skip; String varName = inst.substring(startLoc, inst.indexOf(Lop.VARIABLE_NAME_PLACEHOLDER, startLoc)); String replacement = getVarNameReplacement(inst, varName, map); - inst = inst.replaceAll(Lop.VARIABLE_NAME_PLACEHOLDER + varName + Lop.VARIABLE_NAME_PLACEHOLDER, replacement); + inst = inst.replaceAll(Lop.VARIABLE_NAME_PLACEHOLDER + varName + Lop.VARIABLE_NAME_PLACEHOLDER, + replacement); } } return inst; } - + /** - * Computes the replacement string for a given variable name placeholder string - * (e.g., ##mVar2## or ##Var5##). The replacement is a HDFS filename for matrix - * variables, and is the actual value (stored in symbol table) for scalar variables. + * Computes the replacement string for a given variable name placeholder string + * (e.g., ##mVar2## or ##Var5##). The replacement is a HDFS filename for matrix + * variables, and is the actual value (stored in symbol table) for scalar + * variables. * - * @param inst instruction + * @param inst instruction * @param varName variable name - * @param map local variable map + * @param map local variable map * @return string variable name */ private static String getVarNameReplacement(String inst, String varName, LocalVariableMap map) { @@ -186,7 +190,8 @@ private static String getVarNameReplacement(String inst, String varName, LocalVa replacement = "" + ((ScalarObject) val).getStringValue(); return replacement; } else { - throw new DMLRuntimeException("Variable (" + varName + ") in Instruction (" + inst + ") is not found in the variablemap."); + throw new DMLRuntimeException( + "Variable (" + varName + ") in Instruction (" + inst + ") is not found in the variablemap."); } } } diff --git a/src/main/java/org/apache/sysds/runtime/instructions/cp/MultiReturnBuiltinCPInstruction.java b/src/main/java/org/apache/sysds/runtime/instructions/cp/MultiReturnBuiltinCPInstruction.java index 2633f1151dc..a2497d07a2e 100644 --- a/src/main/java/org/apache/sysds/runtime/instructions/cp/MultiReturnBuiltinCPInstruction.java +++ b/src/main/java/org/apache/sysds/runtime/instructions/cp/MultiReturnBuiltinCPInstruction.java @@ -43,119 +43,114 @@ private MultiReturnBuiltinCPInstruction(Operator op, CPOperand input1, ArrayList super(CPType.MultiReturnBuiltin, op, input1, null, outputs.get(0), opcode, istr); _outputs = outputs; } - + public CPOperand getOutput(int i) { return _outputs.get(i); } - public List getOutputs(){ + public List getOutputs() { return _outputs; } - public String[] getOutputNames(){ + public String[] getOutputNames() { return _outputs.parallelStream().map(output -> output.getName()).toArray(String[]::new); } - - public static MultiReturnBuiltinCPInstruction parseInstruction ( String str ) { + + public static MultiReturnBuiltinCPInstruction parseInstruction(String str) { String[] parts = InstructionUtils.getInstructionPartsWithValueType(str); ArrayList outputs = new ArrayList<>(); // first part is always the opcode String opcode = parts[0]; - - if ( opcode.equalsIgnoreCase("qr") ) { + + if (opcode.equalsIgnoreCase("qr")) { // one input and two ouputs CPOperand in1 = new CPOperand(parts[1]); - outputs.add ( new CPOperand(parts[2], ValueType.FP64, DataType.MATRIX) ); - outputs.add ( new CPOperand(parts[3], ValueType.FP64, DataType.MATRIX) ); - + outputs.add(new CPOperand(parts[2], ValueType.FP64, DataType.MATRIX)); + outputs.add(new CPOperand(parts[3], ValueType.FP64, DataType.MATRIX)); + return new MultiReturnBuiltinCPInstruction(null, in1, outputs, opcode, str); - } - else if ( opcode.equalsIgnoreCase("lu") ) { + } else if (opcode.equalsIgnoreCase("lu")) { CPOperand in1 = new CPOperand(parts[1]); - + // one input and three outputs - outputs.add ( new CPOperand(parts[2], ValueType.FP64, DataType.MATRIX) ); - outputs.add ( new CPOperand(parts[3], ValueType.FP64, DataType.MATRIX) ); - outputs.add ( new CPOperand(parts[4], ValueType.FP64, DataType.MATRIX) ); - - return new MultiReturnBuiltinCPInstruction(null, in1, outputs, opcode, str); - - } - else if ( opcode.equalsIgnoreCase("eigen") ) { - // one input and two outputs - CPOperand in1 = new CPOperand(parts[1]); - outputs.add ( new CPOperand(parts[2], ValueType.FP64, DataType.MATRIX) ); - outputs.add ( new CPOperand(parts[3], ValueType.FP64, DataType.MATRIX) ); - + outputs.add(new CPOperand(parts[2], ValueType.FP64, DataType.MATRIX)); + outputs.add(new CPOperand(parts[3], ValueType.FP64, DataType.MATRIX)); + outputs.add(new CPOperand(parts[4], ValueType.FP64, DataType.MATRIX)); + return new MultiReturnBuiltinCPInstruction(null, in1, outputs, opcode, str); - - } - else if ( opcode.equalsIgnoreCase("fft") ) { + + } else if (opcode.equalsIgnoreCase("eigen")) { // one input and two outputs CPOperand in1 = new CPOperand(parts[1]); - outputs.add ( new CPOperand(parts[2], ValueType.FP64, DataType.MATRIX) ); - outputs.add ( new CPOperand(parts[3], ValueType.FP64, DataType.MATRIX) ); + outputs.add(new CPOperand(parts[2], ValueType.FP64, DataType.MATRIX)); + outputs.add(new CPOperand(parts[3], ValueType.FP64, DataType.MATRIX)); return new MultiReturnBuiltinCPInstruction(null, in1, outputs, opcode, str); - } - else if ( opcode.equalsIgnoreCase("ifft") ) { + } else if (opcode.equalsIgnoreCase("fft")) { // one input and two outputs CPOperand in1 = new CPOperand(parts[1]); - outputs.add ( new CPOperand(parts[2], ValueType.FP64, DataType.MATRIX) ); - outputs.add ( new CPOperand(parts[3], ValueType.FP64, DataType.MATRIX) ); + outputs.add(new CPOperand(parts[2], ValueType.FP64, DataType.MATRIX)); + outputs.add(new CPOperand(parts[3], ValueType.FP64, DataType.MATRIX)); return new MultiReturnBuiltinCPInstruction(null, in1, outputs, opcode, str); - } - else if ( opcode.equalsIgnoreCase("svd") ) { + + // } else if (opcode.equalsIgnoreCase("ifft")) { + // // one input and two outputs + // CPOperand in1 = new CPOperand(parts[1]); + // CPOperand in2 = new CPOperand(parts[2]); + // outputs.add(new CPOperand(parts[3], ValueType.FP64, DataType.MATRIX)); + // outputs.add(new CPOperand(parts[4], ValueType.FP64, DataType.MATRIX)); + + // return new MultiReturnBuiltinCPInstruction(null, in1, in2, outputs, opcode, + // str); + } else if (opcode.equalsIgnoreCase("svd")) { CPOperand in1 = new CPOperand(parts[1]); // one input and three outputs - outputs.add ( new CPOperand(parts[2], ValueType.FP64, DataType.MATRIX) ); - outputs.add ( new CPOperand(parts[3], ValueType.FP64, DataType.MATRIX) ); - outputs.add ( new CPOperand(parts[4], ValueType.FP64, DataType.MATRIX) ); - + outputs.add(new CPOperand(parts[2], ValueType.FP64, DataType.MATRIX)); + outputs.add(new CPOperand(parts[3], ValueType.FP64, DataType.MATRIX)); + outputs.add(new CPOperand(parts[4], ValueType.FP64, DataType.MATRIX)); + return new MultiReturnBuiltinCPInstruction(null, in1, outputs, opcode, str); - } - else { + } else { throw new DMLRuntimeException("Invalid opcode in MultiReturnBuiltin instruction: " + opcode); } } - + public int getNumOutputs() { return _outputs.size(); } - @Override + @Override public void processInstruction(ExecutionContext ec) { - if(!LibCommonsMath.isSupportedMultiReturnOperation(getOpcode())) + if (!LibCommonsMath.isSupportedMultiReturnOperation(getOpcode())) throw new DMLRuntimeException("Invalid opcode in MultiReturnBuiltin instruction: " + getOpcode()); - + MatrixBlock in = ec.getMatrixInput(input1.getName()); MatrixBlock[] out = LibCommonsMath.multiReturnOperations(in, getOpcode()); ec.releaseMatrixInput(input1.getName()); - for(int i=0; i < _outputs.size(); i++) { + for (int i = 0; i < _outputs.size(); i++) { ec.setMatrixOutput(_outputs.get(i).getName(), out[i]); } } - + @Override public boolean hasSingleLineage() { return false; } - @Override @SuppressWarnings("unchecked") public Pair[] getLineageItems(ExecutionContext ec) { LineageItem[] inputLineage = LineageItemUtils.getLineage(ec, input1, input2, input3); - final Pair[] ret = new Pair[_outputs.size()]; - for(int i = 0; i < _outputs.size(); i++){ + final Pair[] ret = new Pair[_outputs.size()]; + for (int i = 0; i < _outputs.size(); i++) { CPOperand out = _outputs.get(i); ret[i] = Pair.of(out.getName(), new LineageItem(getOpcode(), inputLineage)); } - return ret; + return ret; } } diff --git a/src/main/java/org/apache/sysds/runtime/instructions/cp/MultiReturnMatrixMatrixBuiltinCPInstruction.java b/src/main/java/org/apache/sysds/runtime/instructions/cp/MultiReturnMatrixMatrixBuiltinCPInstruction.java new file mode 100644 index 00000000000..d4a02173a28 --- /dev/null +++ b/src/main/java/org/apache/sysds/runtime/instructions/cp/MultiReturnMatrixMatrixBuiltinCPInstruction.java @@ -0,0 +1,115 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.sysds.runtime.instructions.cp; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.lang3.tuple.Pair; +import org.apache.sysds.common.Types.DataType; +import org.apache.sysds.common.Types.ValueType; +import org.apache.sysds.runtime.DMLRuntimeException; +import org.apache.sysds.runtime.controlprogram.context.ExecutionContext; +import org.apache.sysds.runtime.instructions.InstructionUtils; +import org.apache.sysds.runtime.lineage.LineageItem; +import org.apache.sysds.runtime.lineage.LineageItemUtils; +import org.apache.sysds.runtime.matrix.data.LibCommonsMath; +import org.apache.sysds.runtime.matrix.data.MatrixBlock; +import org.apache.sysds.runtime.matrix.operators.Operator; + +public class MultiReturnMatrixMatrixBuiltinCPInstruction extends ComputationCPInstruction { + + protected ArrayList _outputs; + + private MultiReturnMatrixMatrixBuiltinCPInstruction(Operator op, CPOperand input1, CPOperand input2, + ArrayList outputs, String opcode, + String istr) { + super(CPType.MultiReturnBuiltin, op, input1, input2, outputs.get(0), opcode, istr); + _outputs = outputs; + } + + public CPOperand getOutput(int i) { + return _outputs.get(i); + } + + public List getOutputs() { + return _outputs; + } + + public String[] getOutputNames() { + return _outputs.parallelStream().map(output -> output.getName()).toArray(String[]::new); + } + + public static MultiReturnMatrixMatrixBuiltinCPInstruction parseInstruction(String str) { + String[] parts = InstructionUtils.getInstructionPartsWithValueType(str); + ArrayList outputs = new ArrayList<>(); + // first part is always the opcode + String opcode = parts[0]; + + if (opcode.equalsIgnoreCase("ifft")) { + // one input and two outputs + CPOperand in1 = new CPOperand(parts[1]); + CPOperand in2 = new CPOperand(parts[2]); + outputs.add(new CPOperand(parts[3], ValueType.FP64, DataType.MATRIX)); + outputs.add(new CPOperand(parts[4], ValueType.FP64, DataType.MATRIX)); + + return new MultiReturnMatrixMatrixBuiltinCPInstruction(null, in1, in2, outputs, opcode, str); + } else { + throw new DMLRuntimeException("Invalid opcode in MultiReturnBuiltin instruction: " + opcode); + } + + } + + public int getNumOutputs() { + return _outputs.size(); + } + + @Override + public void processInstruction(ExecutionContext ec) { + if (!LibCommonsMath.isSupportedMultiReturnOperation(getOpcode())) + throw new DMLRuntimeException("Invalid opcode in MultiReturnBuiltin instruction: " + getOpcode()); + + MatrixBlock in1 = ec.getMatrixInput(input1.getName()); + MatrixBlock in2 = ec.getMatrixInput(input2.getName()); + MatrixBlock[] out = LibCommonsMath.multiReturnOperations(in1, in2, getOpcode()); + ec.releaseMatrixInput(input1.getName(), input2.getName()); + + for (int i = 0; i < _outputs.size(); i++) { + ec.setMatrixOutput(_outputs.get(i).getName(), out[i]); + } + } + + @Override + public boolean hasSingleLineage() { + return false; + } + + @Override + @SuppressWarnings("unchecked") + public Pair[] getLineageItems(ExecutionContext ec) { + LineageItem[] inputLineage = LineageItemUtils.getLineage(ec, input1, input2, input3); + final Pair[] ret = new Pair[_outputs.size()]; + for (int i = 0; i < _outputs.size(); i++) { + CPOperand out = _outputs.get(i); + ret[i] = Pair.of(out.getName(), new LineageItem(getOpcode(), inputLineage)); + } + return ret; + } +} diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java index d18c0c40e70..d3491a8f986 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java @@ -104,8 +104,13 @@ public static MatrixBlock[] multiReturnOperations(MatrixBlock in, String opcode) return multiReturnOperations(in, opcode, 1, 1); } - public static MatrixBlock[] multiReturnOperations(MatrixBlock in, String opcode, int threads, int num_iterations, double tol) { - if(opcode.equals("eigen_qr")) + public static MatrixBlock[] multiReturnOperations(MatrixBlock in1, MatrixBlock in2, String opcode) { + return multiReturnOperations(in1, in2, opcode, 1, 1); + } + + public static MatrixBlock[] multiReturnOperations(MatrixBlock in, String opcode, int threads, int num_iterations, + double tol) { + if (opcode.equals("eigen_qr")) return computeEigenQR(in, num_iterations, tol, threads); else return multiReturnOperations(in, opcode, threads, 1); @@ -114,29 +119,53 @@ public static MatrixBlock[] multiReturnOperations(MatrixBlock in, String opcode, public static MatrixBlock[] multiReturnOperations(MatrixBlock in, String opcode, int threads, long seed) { switch (opcode) { - case "qr": return computeQR(in); - case "qr2": return computeQR2(in, threads); - case "lu": return computeLU(in); - case "eigen": return computeEigen(in); - case "eigen_lanczos": return computeEigenLanczos(in, threads, seed); - case "eigen_qr": return computeEigenQR(in, threads); - case "fft": return computeFFT(in); - case "ifft": return computeIFFT(in); - case "svd": return computeSvd(in); - default: return null; + case "qr": + return computeQR(in); + case "qr2": + return computeQR2(in, threads); + case "lu": + return computeLU(in); + case "eigen": + return computeEigen(in); + case "eigen_lanczos": + return computeEigenLanczos(in, threads, seed); + case "eigen_qr": + return computeEigenQR(in, threads); + case "fft": + return computeFFT(in); + // TODO: add ifft for only one input + // case "ifft": + // return computeIFFT(in); + case "svd": + return computeSvd(in); + default: + return null; } } - + + public static MatrixBlock[] multiReturnOperations(MatrixBlock in1, MatrixBlock in2, String opcode, int threads, + long seed) { + + switch (opcode) { + case "ifft": + return computeIFFT(in1, in2); + default: + return null; + } + + } + public static MatrixBlock matrixMatrixOperations(MatrixBlock in1, MatrixBlock in2, String opcode) { - if(opcode.equals("solve")) { + if (opcode.equals("solve")) { if (in1.getNumRows() != in1.getNumColumns()) throw new DMLRuntimeException("The A matrix, in solve(A,b) should have squared dimensions."); return computeSolve(in1, in2); } + return null; } - + /** * Function to solve a given system of equations. * @@ -271,11 +300,12 @@ private static EigenDecomposition computeEigenRegularized(MatrixBlock in) { * @return array of matrix blocks */ private static MatrixBlock[] computeFFT(MatrixBlock in) { - if( in == null || in.isEmptyBlock(false) ) + if (in == null || in.isEmptyBlock(false)) throw new DMLRuntimeException("Invalid empty block"); - //run fft + // run fft in.sparseToDense(); + System.out.println("calling fft now"); return fft(in); } @@ -286,20 +316,37 @@ private static MatrixBlock[] computeFFT(MatrixBlock in) { * @return array of matrix blocks */ private static MatrixBlock[] computeIFFT(MatrixBlock in) { - if( in == null || in.isEmptyBlock(false)) + if (in == null || in.isEmptyBlock(false)) throw new DMLRuntimeException("Invalid empty block"); - //run ifft + // run ifft in.sparseToDense(); return ifft(in); } + /** + * Function to perform IFFT on a given matrix. + * + * @param in matrix object + * @return array of matrix blocks + */ + private static MatrixBlock[] computeIFFT(MatrixBlock in1, MatrixBlock in2) { + if (in1 == null || in1.isEmptyBlock(false) || in2 == null || in2.isEmptyBlock(false)) + throw new DMLRuntimeException("Invalid empty block"); + + // run ifft + in1.sparseToDense(); + in2.sparseToDense(); + return ifft(in1, in2); + } + /** * Performs Singular Value Decomposition. Calls Apache Commons Math SVD. * X = U * Sigma * Vt, where X is the input matrix, - * U is the left singular matrix, Sigma is the singular values matrix returned as a + * U is the left singular matrix, Sigma is the singular values matrix returned + * as a * column matrix and Vt is the transpose of the right singular matrix V. - * However, the returned array has { U, Sigma, V} + * However, the returned array has { U, Sigma, V} * * @param in Input matrix * @return An array containing U, Sigma & V diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java index 2c9fd8339dd..662313cfd90 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java @@ -25,117 +25,135 @@ public class LibMatrixFourier { /** * Function to perform FFT on two given matrices. - * The first one represents the real values and the second one the imaginary values. - * The output also contains one matrix for the real and one for the imaginary values. + * The first one represents the real values and the second one the imaginary + * values. + * The output also contains one matrix for the real and one for the imaginary + * values. * * @param re matrix object representing the real values * @param im matrix object representing the imaginary values * @return array of two matrix blocks */ - public static MatrixBlock[] fft(MatrixBlock re, MatrixBlock im){ + public static MatrixBlock[] fft(MatrixBlock re, MatrixBlock im) { int rows = re.getNumRows(); int cols = re.getNumColumns(); - if(!isPowerOfTwo(rows) || !isPowerOfTwo(cols)) throw new RuntimeException("false dimensions"); + if (!isPowerOfTwo(rows) || !isPowerOfTwo(cols)) + throw new RuntimeException("false dimensions"); fft(re.getDenseBlockValues(), im.getDenseBlockValues(), rows, cols); + System.out.println("fft result in java function:"); + System.out.println((new MatrixBlock[] { re, im })[0].toString()); + System.out.println("\n\n"); - return new MatrixBlock[]{re, im}; + return new MatrixBlock[] { re, im }; } /** * Function to perform IFFT on two given matrices. - * The first one represents the real values and the second one the imaginary values. - * The output also contains one matrix for the real and one for the imaginary values. + * The first one represents the real values and the second one the imaginary + * values. + * The output also contains one matrix for the real and one for the imaginary + * values. * * @param re matrix object representing the real values * @param im matrix object representing the imaginary values * @return array of two matrix blocks */ - public static MatrixBlock[] ifft(MatrixBlock re, MatrixBlock im){ + public static MatrixBlock[] ifft(MatrixBlock re, MatrixBlock im) { int rows = re.getNumRows(); int cols = re.getNumColumns(); - if(!isPowerOfTwo(rows) || !isPowerOfTwo(cols)) throw new RuntimeException("false dimensions"); + if (!isPowerOfTwo(rows) || !isPowerOfTwo(cols)) + throw new RuntimeException("false dimensions"); ifft(re.getDenseBlockValues(), im.getDenseBlockValues(), rows, cols); - return new MatrixBlock[]{re, im}; + System.out.println("IFFT result in function"); + System.out.println((new MatrixBlock[] { re, im })[0].toString()); + System.out.println("\n\n"); + + return new MatrixBlock[] { re, im }; } /** * Function to perform FFT on two given double arrays. - * The first one represents the real values and the second one the imaginary values. + * The first one represents the real values and the second one the imaginary + * values. * Both arrays get updated and contain the result. * - * @param re array representing the real values - * @param im array representing the imaginary values + * @param re array representing the real values + * @param im array representing the imaginary values * @param rows number of rows * @param cols number of rows */ public static void fft(double[] re, double[] im, int rows, int cols) { - double[] re_inter = new double[rows*cols]; - double[] im_inter = new double[rows*cols]; + double[] re_inter = new double[rows * cols]; + double[] im_inter = new double[rows * cols]; - for(int i = 0; i < rows; i++){ - fft_one_dim(re, im, re_inter, im_inter, i*cols, (i+1)*cols, cols, 1); + for (int i = 0; i < rows; i++) { + fft_one_dim(re, im, re_inter, im_inter, i * cols, (i + 1) * cols, cols, 1); } - for(int j = 0; j < cols; j++){ - fft_one_dim(re, im, re_inter, im_inter, j, j+rows*cols, rows, cols); + for (int j = 0; j < cols; j++) { + fft_one_dim(re, im, re_inter, im_inter, j, j + rows * cols, rows, cols); } } /** * Function to perform IFFT on two given double arrays. - * The first one represents the real values and the second one the imaginary values. + * The first one represents the real values and the second one the imaginary + * values. * Both arrays get updated and contain the result. * - * @param re array representing the real values - * @param im array representing the imaginary values + * @param re array representing the real values + * @param im array representing the imaginary values * @param rows number of rows * @param cols number of rows */ public static void ifft(double[] re, double[] im, int rows, int cols) { - double[] re_inter = new double[rows*cols]; - double[] im_inter = new double[rows*cols]; + double[] re_inter = new double[rows * cols]; + double[] im_inter = new double[rows * cols]; - for(int j = 0; j < cols; j++){ - ifft_one_dim(re, im, re_inter, im_inter, j, j+rows*cols, rows, cols); + for (int j = 0; j < cols; j++) { + ifft_one_dim(re, im, re_inter, im_inter, j, j + rows * cols, rows, cols); } - for(int i = 0; i < rows; i++){ - ifft_one_dim(re, im, re_inter, im_inter, i*cols, (i+1)*cols, cols, 1); + for (int i = 0; i < rows; i++) { + ifft_one_dim(re, im, re_inter, im_inter, i * cols, (i + 1) * cols, cols, 1); } } - public static void fft_one_dim(double[] re, double[] im, double[] re_inter, double[] im_inter, int start, int stop, int num, int minStep) { + public static void fft_one_dim(double[] re, double[] im, double[] re_inter, double[] im_inter, int start, int stop, + int num, int minStep) { // start inclusive, stop exclusive - if(num == 1) return; + if (num == 1) + return; // even indices - for(int step = minStep*(num/2), subNum = 2; subNum <= num; step/=2, subNum*=2){ + for (int step = minStep * (num / 2), subNum = 2; subNum <= num; step /= 2, subNum *= 2) { - double angle = -2*FastMath.PI/subNum; + double angle = -2 * FastMath.PI / subNum; // use ceil for the main (sub)array - for(int sub = 0; sub < FastMath.ceil(num/(2*(double)subNum)); sub++){ + for (int sub = 0; sub < FastMath.ceil(num / (2 * (double) subNum)); sub++) { - for(int isOdd = 0; isOdd < 2; isOdd++) { + for (int isOdd = 0; isOdd < 2; isOdd++) { // no odd values for main (sub)array - if (isOdd == 1 && subNum == num) return; + if (isOdd == 1 && subNum == num) + return; - int startSub = start + sub*minStep + isOdd*(step/2); + int startSub = start + sub * minStep + isOdd * (step / 2); // first iterates over even indices, then over odd indices - for (int j = startSub, cnt = 0; cnt < subNum / 2; j += 2*step, cnt++) { + for (int j = startSub, cnt = 0; cnt < subNum / 2; j += 2 * step, cnt++) { double omega_pow_re = FastMath.cos(cnt * angle); double omega_pow_im = FastMath.sin(cnt * angle); @@ -146,13 +164,13 @@ public static void fft_one_dim(double[] re, double[] im, double[] re_inter, doub int index = startSub + cnt * step; re_inter[index] = re[j] + m_re; - re_inter[index + (stop-start)/2] = re[j] - m_re; + re_inter[index + (stop - start) / 2] = re[j] - m_re; im_inter[index] = im[j] + m_im; - im_inter[index + (stop-start)/2] = im[j] - m_im; + im_inter[index + (stop - start) / 2] = im[j] - m_im; } - for (int j = startSub; j < startSub + (stop-start); j += step) { + for (int j = startSub; j < startSub + (stop - start); j += step) { re[j] = re_inter[j]; im[j] = im_inter[j]; re_inter[j] = 0; @@ -167,21 +185,23 @@ public static void fft_one_dim(double[] re, double[] im, double[] re_inter, doub } - private static void ifft_one_dim(double[] re, double[] im, double[] re_inter, double[] im_inter, int start, int stop, int num, int minStep) { + private static void ifft_one_dim(double[] re, double[] im, double[] re_inter, double[] im_inter, int start, + int stop, int num, int minStep) { // conjugate input - for (int i = start; i < start+num*minStep; i+=minStep){ + for (int i = start; i < start + num * minStep; i += minStep) { im[i] = -im[i]; } // apply fft - //fft_one_dim_recursive(re, im, re_inter, im_inter, start, step, num, subArraySize); + // fft_one_dim_recursive(re, im, re_inter, im_inter, start, step, num, + // subArraySize); fft_one_dim(re, im, re_inter, im_inter, start, stop, num, minStep); // conjugate and scale result - for (int i = start; i < start+num*minStep; i+=minStep){ - re[i] = re[i]/num; - im[i] = -im[i]/num; + for (int i = start; i < start + num * minStep; i += minStep) { + re[i] = re[i] / num; + im[i] = -im[i] / num; } } @@ -192,7 +212,7 @@ private static void ifft_one_dim(double[] re, double[] im, double[] re_inter, do * @param n integer to check * @return true if n is a power of two, false otherwise */ - public static boolean isPowerOfTwo(int n){ + public static boolean isPowerOfTwo(int n) { return (n != 0) && ((n & (n - 1)) == 0); } @@ -204,8 +224,9 @@ public static boolean isPowerOfTwo(int n){ * @param re matrix object representing the real values * @return array of two matrix blocks */ - public static MatrixBlock[] fft(MatrixBlock re){ - return fft(re, new MatrixBlock(re.getNumRows(),re.getNumColumns(), new double[re.getNumRows()*re.getNumColumns()])); + public static MatrixBlock[] fft(MatrixBlock re) { + return fft(re, + new MatrixBlock(re.getNumRows(), re.getNumColumns(), new double[re.getNumRows() * re.getNumColumns()])); } /** @@ -216,8 +237,9 @@ public static MatrixBlock[] fft(MatrixBlock re){ * @param re matrix object representing the real values * @return array of two matrix blocks */ - public static MatrixBlock[] ifft(MatrixBlock re){ - return ifft(re, new MatrixBlock(re.getNumRows(),re.getNumColumns(), new double[re.getNumRows()*re.getNumColumns()])); + public static MatrixBlock[] ifft(MatrixBlock re) { + return ifft(re, + new MatrixBlock(re.getNumRows(), re.getNumColumns(), new double[re.getNumRows() * re.getNumColumns()])); } } diff --git a/src/main/java/org/apache/sysds/runtime/privacy/propagation/PrivacyPropagator.java b/src/main/java/org/apache/sysds/runtime/privacy/propagation/PrivacyPropagator.java index 09c6a8786a1..26aadd17687 100644 --- a/src/main/java/org/apache/sysds/runtime/privacy/propagation/PrivacyPropagator.java +++ b/src/main/java/org/apache/sysds/runtime/privacy/propagation/PrivacyPropagator.java @@ -70,24 +70,23 @@ import org.apache.wink.json4j.JSONObject; /** - * Class with static methods merging privacy constraints of operands - * in expressions to generate the privacy constraints of the output. + * Class with static methods merging privacy constraints of operands + * in expressions to generate the privacy constraints of the output. */ -public class PrivacyPropagator -{ +public class PrivacyPropagator { /** * Parses the privacy constraint of the given metadata object * and sets the field of the given Data if the privacy constraint is not null. - * @param cd data for which privacy constraint is set + * + * @param cd data for which privacy constraint is set * @param mtd metadata object * @return data object with privacy constraint set * @throws JSONException during parsing of metadata */ public static Data parseAndSetPrivacyConstraint(Data cd, JSONObject mtd) - throws JSONException - { + throws JSONException { PrivacyConstraint mtdPrivConstraint = parseAndReturnPrivacyConstraint(mtd); - if ( mtdPrivConstraint != null ) + if (mtdPrivConstraint != null) cd.setPrivacyConstraints(mtdPrivConstraint); return cd; } @@ -95,94 +94,102 @@ public static Data parseAndSetPrivacyConstraint(Data cd, JSONObject mtd) /** * Parses the privacy constraint of the given metadata object * or returns null if no privacy constraint is set in the metadata. + * * @param mtd metadata * @return privacy constraint parsed from metadata object * @throws JSONException during parsing of metadata */ public static PrivacyConstraint parseAndReturnPrivacyConstraint(JSONObject mtd) - throws JSONException - { - if ( mtd.containsKey(DataExpression.PRIVACY) ) { + throws JSONException { + if (mtd.containsKey(DataExpression.PRIVACY)) { String privacyLevel = mtd.getString(DataExpression.PRIVACY); - if ( privacyLevel != null ) + if (privacyLevel != null) return new PrivacyConstraint(PrivacyLevel.valueOf(privacyLevel)); } return null; } - private static boolean anyInputHasLevel(PrivacyLevel[] inputLevels, PrivacyLevel targetLevel){ + private static boolean anyInputHasLevel(PrivacyLevel[] inputLevels, PrivacyLevel targetLevel) { return Arrays.stream(inputLevels).anyMatch(i -> i == targetLevel); } /** - * Returns the output privacy level based on the given input privacy levels and operator type. + * Returns the output privacy level based on the given input privacy levels and + * operator type. * It represents the basic logic of privacy propagation: * * Unary input: - * Input | NonAggregate | Aggregate + * Input | NonAggregate | Aggregate * ----------------------------------- - * priv | priv | priv - * privAgg | privAgg | none - * none | none | none + * priv | priv | priv + * privAgg | privAgg | none + * none | none | none * * Binary input: - * Input | NonAggregate | Aggregate + * Input | NonAggregate | Aggregate * -------------------------------------------- - * priv-priv | priv | priv - * priv-privAgg | priv | priv - * priv-none | priv | priv - * privAgg-priv | priv | priv - * none-priv | priv | priv - * privAgg-privAgg | privAgg | none - * none-none | none | none - * privAgg-none | privAgg | none - * none-privAgg | privAgg | none + * priv-priv | priv | priv + * priv-privAgg | priv | priv + * priv-none | priv | priv + * privAgg-priv | priv | priv + * none-priv | priv | priv + * privAgg-privAgg | privAgg | none + * none-none | none | none + * privAgg-none | privAgg | none + * none-privAgg | privAgg | none * - * @param inputLevels privacy levels of the input - * @param operatorType type of the operator which is either an aggregation (Aggregate) or not an aggregation (NonAggregate) + * @param inputLevels privacy levels of the input + * @param operatorType type of the operator which is either an aggregation + * (Aggregate) or not an aggregation (NonAggregate) * @return output privacy level */ - public static PrivacyLevel corePropagation(PrivacyLevel[] inputLevels, OperatorType operatorType){ + public static PrivacyLevel corePropagation(PrivacyLevel[] inputLevels, OperatorType operatorType) { if (anyInputHasLevel(inputLevels, PrivacyLevel.Private)) return PrivacyLevel.Private; if (operatorType == OperatorType.Aggregate) return PrivacyLevel.None; - if (operatorType == OperatorType.NonAggregate && anyInputHasLevel(inputLevels,PrivacyLevel.PrivateAggregation)) + if (operatorType == OperatorType.NonAggregate && anyInputHasLevel(inputLevels, PrivacyLevel.PrivateAggregation)) return PrivacyLevel.PrivateAggregation; return PrivacyLevel.None; } /** - * Merges the given privacy constraints with the core propagation using the given operator type. + * Merges the given privacy constraints with the core propagation using the + * given operator type. + * * @param privacyConstraints array of privacy constraints to merge - * @param operatorType type of operation to use when merging with the core propagation + * @param operatorType type of operation to use when merging with the core + * propagation * @return merged privacy constraint */ - private static PrivacyConstraint mergeNary(PrivacyConstraint[] privacyConstraints, OperatorType operatorType){ + private static PrivacyConstraint mergeNary(PrivacyConstraint[] privacyConstraints, OperatorType operatorType) { PrivacyLevel[] privacyLevels = Arrays.stream(privacyConstraints) - .map(constraint -> { - if (constraint != null) - return constraint.getPrivacyLevel(); - else return PrivacyLevel.None; - }) - .toArray(PrivacyLevel[]::new); + .map(constraint -> { + if (constraint != null) + return constraint.getPrivacyLevel(); + else + return PrivacyLevel.None; + }) + .toArray(PrivacyLevel[]::new); PrivacyLevel outputPrivacyLevel = corePropagation(privacyLevels, operatorType); return new PrivacyConstraint(outputPrivacyLevel); } /** - * Merges the input privacy constraints using the core propagation with NonAggregate operator type. + * Merges the input privacy constraints using the core propagation with + * NonAggregate operator type. + * * @param privacyConstraint1 first privacy constraint * @param privacyConstraint2 second privacy constraint * @return merged privacy constraint */ - public static PrivacyConstraint mergeBinary(PrivacyConstraint privacyConstraint1, PrivacyConstraint privacyConstraint2) { - if (privacyConstraint1 != null && privacyConstraint2 != null){ - PrivacyLevel[] privacyLevels = new PrivacyLevel[]{ - privacyConstraint1.getPrivacyLevel(),privacyConstraint2.getPrivacyLevel()}; + public static PrivacyConstraint mergeBinary(PrivacyConstraint privacyConstraint1, + PrivacyConstraint privacyConstraint2) { + if (privacyConstraint1 != null && privacyConstraint2 != null) { + PrivacyLevel[] privacyLevels = new PrivacyLevel[] { + privacyConstraint1.getPrivacyLevel(), privacyConstraint2.getPrivacyLevel() }; return new PrivacyConstraint(corePropagation(privacyLevels, OperatorType.NonAggregate)); - } - else if (privacyConstraint1 != null) + } else if (privacyConstraint1 != null) return privacyConstraint1; else if (privacyConstraint2 != null) return privacyConstraint2; @@ -191,40 +198,43 @@ else if (privacyConstraint2 != null) /** * Propagate privacy constraints from input hops to given hop. + * * @param hop which the privacy constraints are propagated to */ - public static void hopPropagation(Hop hop){ + public static void hopPropagation(Hop hop) { hopPropagation(hop, hop.getInput()); } /** * Propagate privacy constraints from input hops to given hop. - * @param hop which the privacy constraints are propagated to + * + * @param hop which the privacy constraints are propagated to * @param inputHops inputs to given hop */ - public static void hopPropagation(Hop hop, ArrayList inputHops){ + public static void hopPropagation(Hop hop, ArrayList inputHops) { PrivacyConstraint[] inputConstraints = inputHops.stream() - .map(Hop::getPrivacy).toArray(PrivacyConstraint[]::new); + .map(Hop::getPrivacy).toArray(PrivacyConstraint[]::new); OperatorType opType = getOpType(hop); hop.setPrivacy(mergeNary(inputConstraints, opType)); if (opType == null && Arrays.stream(inputConstraints).anyMatch(Objects::nonNull)) throw new DMLException("Input has constraint but hop type not recognized by PrivacyPropagator. " + - "Hop is " + hop + " " + hop.getClass()); + "Hop is " + hop + " " + hop.getClass()); } /** * Get operator type of given hop. * Returns null if hop type is not known. + * * @param hop for which operator type is returned * @return operator type of hop or null if hop type is unknown */ - private static OperatorType getOpType(Hop hop){ - if ( hop instanceof TernaryOp || hop instanceof BinaryOp || hop instanceof ReorgOp - || hop instanceof DataOp || hop instanceof LiteralOp || hop instanceof NaryOp - || hop instanceof DataGenOp || hop instanceof FunctionOp || hop instanceof IndexingOp - || hop instanceof ParameterizedBuiltinOp || hop instanceof LeftIndexingOp ) + private static OperatorType getOpType(Hop hop) { + if (hop instanceof TernaryOp || hop instanceof BinaryOp || hop instanceof ReorgOp + || hop instanceof DataOp || hop instanceof LiteralOp || hop instanceof NaryOp + || hop instanceof DataGenOp || hop instanceof FunctionOp || hop instanceof IndexingOp + || hop instanceof ParameterizedBuiltinOp || hop instanceof LeftIndexingOp) return OperatorType.NonAggregate; - else if ( hop instanceof AggBinaryOp || hop instanceof AggUnaryOp || hop instanceof UnaryOp ) + else if (hop instanceof AggBinaryOp || hop instanceof AggUnaryOp || hop instanceof UnaryOp) return OperatorType.Aggregate; else return null; @@ -234,16 +244,17 @@ else if ( hop instanceof AggBinaryOp || hop instanceof AggUnaryOp || hop instan * Propagate privacy constraints to output variables * based on privacy constraint of CPOperand output in instruction * which has been set during privacy propagation preprocessing. + * * @param inst instruction for which privacy constraints are propagated - * @param ec execution context + * @param ec execution context */ - public static void postProcessInstruction(Instruction inst, ExecutionContext ec){ + public static void postProcessInstruction(Instruction inst, ExecutionContext ec) { // if inst has output List instOutputs = getOutputOperands(inst); - if (!instOutputs.isEmpty()){ - for ( CPOperand output : instOutputs ){ + if (!instOutputs.isEmpty()) { + for (CPOperand output : instOutputs) { PrivacyConstraint outputPrivacyConstraint = output.getPrivacyConstraint(); - if ( PrivacyUtils.someConstraintSetUnary(outputPrivacyConstraint) ) + if (PrivacyUtils.someConstraintSetUnary(outputPrivacyConstraint)) setOutputPrivacyConstraint(ec, outputPrivacyConstraint, output.getName()); } } @@ -252,14 +263,16 @@ public static void postProcessInstruction(Instruction inst, ExecutionContext ec) /** * Propagate privacy constraints from input to output CPOperands * in case the privacy constraints of the input are activated. + * * @param inst instruction for which the privacy constraints are propagated - * @param ec execution context - * @return instruction with propagated privacy constraints (usually the same instance as the input inst) + * @param ec execution context + * @return instruction with propagated privacy constraints (usually the same + * instance as the input inst) */ - public static Instruction preprocessInstruction(Instruction inst, ExecutionContext ec){ - switch ( inst.getType() ){ + public static Instruction preprocessInstruction(Instruction inst, ExecutionContext ec) { + switch (inst.getType()) { case CONTROL_PROGRAM: - return preprocessCPInstruction( (CPInstruction) inst, ec ); + return preprocessCPInstruction((CPInstruction) inst, ec); case BREAKPOINT: case SPARK: case GPU: @@ -270,8 +283,8 @@ public static Instruction preprocessInstruction(Instruction inst, ExecutionConte } } - private static Instruction preprocessCPInstruction(CPInstruction inst, ExecutionContext ec){ - switch(inst.getCPInstructionType()){ + private static Instruction preprocessCPInstruction(CPInstruction inst, ExecutionContext ec) { + switch (inst.getCPInstructionType()) { case Binary: case Builtin: case BuiltinNary: @@ -282,18 +295,20 @@ private static Instruction preprocessCPInstruction(CPInstruction inst, Execution case Ternary: case Unary: case MultiReturnBuiltin: + case MultiReturnMatrixMatrixBuiltin: case MultiReturnParameterizedBuiltin: case MatrixIndexing: - return mergePrivacyConstraintsFromInput( inst, ec, OperatorType.NonAggregate ); + return mergePrivacyConstraintsFromInput(inst, ec, OperatorType.NonAggregate); case AggregateTernary: case AggregateUnary: return mergePrivacyConstraintsFromInput(inst, ec, OperatorType.Aggregate); case Append: return preprocessAppendCPInstruction((AppendCPInstruction) inst, ec); case AggregateBinary: - if ( inst instanceof AggregateBinaryCPInstruction ) - return preprocessAggregateBinaryCPInstruction((AggregateBinaryCPInstruction)inst, ec); - else return throwExceptionIfInputOrInstPrivacy(inst, ec); + if (inst instanceof AggregateBinaryCPInstruction) + return preprocessAggregateBinaryCPInstruction((AggregateBinaryCPInstruction) inst, ec); + else + return throwExceptionIfInputOrInstPrivacy(inst, ec); case MMTSJ: OperatorType mmtsjOpType = OperatorType.getAggregationType((MMTSJCPInstruction) inst, ec); return mergePrivacyConstraintsFromInput(inst, ec, mmtsjOpType); @@ -307,8 +322,8 @@ private static Instruction preprocessCPInstruction(CPInstruction inst, Execution } } - private static Instruction preprocessVariableCPInstruction(VariableCPInstruction inst, ExecutionContext ec){ - switch ( inst.getVariableOpcode() ) { + private static Instruction preprocessVariableCPInstruction(VariableCPInstruction inst, ExecutionContext ec) { + switch (inst.getVariableOpcode()) { case CopyVariable: case MoveVariable: case RemoveVariableAndFile: @@ -325,9 +340,10 @@ private static Instruction preprocessVariableCPInstruction(VariableCPInstruction return propagateSecondInputPrivacy(inst, ec); case AssignVariable: case RemoveVariable: - return mergePrivacyConstraintsFromInput( inst, ec, OperatorType.NonAggregate ); + return mergePrivacyConstraintsFromInput(inst, ec, OperatorType.NonAggregate); case Read: - // Adds scalar object to variable map, hence input (data type and filename) privacy should not be propagated + // Adds scalar object to variable map, hence input (data type and filename) + // privacy should not be propagated return inst; default: return throwExceptionIfInputOrInstPrivacy(inst, ec); @@ -337,23 +353,26 @@ private static Instruction preprocessVariableCPInstruction(VariableCPInstruction /** * Propagates fine-grained constraints if input has fine-grained constraints, * otherwise it propagates general constraints. + * * @param inst aggregate binary instruction for which constraints are propagated - * @param ec execution context - * @return instruction with merged privacy constraints propagated to it and output CPOperand + * @param ec execution context + * @return instruction with merged privacy constraints propagated to it and + * output CPOperand */ - private static Instruction preprocessAggregateBinaryCPInstruction(AggregateBinaryCPInstruction inst, ExecutionContext ec){ + private static Instruction preprocessAggregateBinaryCPInstruction(AggregateBinaryCPInstruction inst, + ExecutionContext ec) { PrivacyConstraint[] privacyConstraints = getInputPrivacyConstraints(ec, inst.getInputs()); - if ( PrivacyUtils.someConstraintSetBinary(privacyConstraints) ){ + if (PrivacyUtils.someConstraintSetBinary(privacyConstraints)) { PrivacyConstraint mergedPrivacyConstraint; - if ( (privacyConstraints[0] != null && privacyConstraints[0].hasFineGrainedConstraints() ) || - (privacyConstraints[1] != null && privacyConstraints[1].hasFineGrainedConstraints() )){ + if ((privacyConstraints[0] != null && privacyConstraints[0].hasFineGrainedConstraints()) || + (privacyConstraints[1] != null && privacyConstraints[1].hasFineGrainedConstraints())) { MatrixBlock input1 = ec.getMatrixInput(inst.input1.getName()); MatrixBlock input2 = ec.getMatrixInput(inst.input2.getName()); - Propagator propagator = new MatrixMultiplicationPropagatorPrivateFirst(input1, privacyConstraints[0], input2, privacyConstraints[1]); + Propagator propagator = new MatrixMultiplicationPropagatorPrivateFirst(input1, privacyConstraints[0], + input2, privacyConstraints[1]); mergedPrivacyConstraint = propagator.propagate(); ec.releaseMatrixInput(inst.input1.getName(), inst.input2.getName()); - } - else { + } else { mergedPrivacyConstraint = mergeNary(privacyConstraints, OperatorType.getAggregationType(inst, ec)); inst.setPrivacyConstraint(mergedPrivacyConstraint); } @@ -363,44 +382,50 @@ private static Instruction preprocessAggregateBinaryCPInstruction(AggregateBinar } /** - * Propagates input privacy constraints using general and fine-grained constraints depending on the AppendType. + * Propagates input privacy constraints using general and fine-grained + * constraints depending on the AppendType. + * * @param inst append instruction for which constraints are propagated - * @param ec execution context - * @return instruction with merged privacy constraints propagated to it and output CPOperand + * @param ec execution context + * @return instruction with merged privacy constraints propagated to it and + * output CPOperand */ - private static Instruction preprocessAppendCPInstruction(AppendCPInstruction inst, ExecutionContext ec){ + private static Instruction preprocessAppendCPInstruction(AppendCPInstruction inst, ExecutionContext ec) { PrivacyConstraint[] privacyConstraints = getInputPrivacyConstraints(ec, inst.getInputs()); - if ( PrivacyUtils.someConstraintSetBinary(privacyConstraints) ){ - if ( inst.getAppendType() == AppendCPInstruction.AppendType.STRING ){ + if (PrivacyUtils.someConstraintSetBinary(privacyConstraints)) { + if (inst.getAppendType() == AppendCPInstruction.AppendType.STRING) { PrivacyLevel[] privacyLevels = new PrivacyLevel[2]; privacyLevels[0] = PrivacyUtils.getGeneralPrivacyLevel(privacyConstraints[0]); - privacyLevels[1] = PrivacyUtils.getGeneralPrivacyLevel(privacyConstraints[1]); - PrivacyConstraint outputConstraint = new PrivacyConstraint(corePropagation(privacyLevels, OperatorType.NonAggregate)); + privacyLevels[1] = PrivacyUtils.getGeneralPrivacyLevel(privacyConstraints[1]); + PrivacyConstraint outputConstraint = new PrivacyConstraint( + corePropagation(privacyLevels, OperatorType.NonAggregate)); inst.output.setPrivacyConstraint(outputConstraint); - } else if ( inst.getAppendType() == AppendCPInstruction.AppendType.LIST ){ + } else if (inst.getAppendType() == AppendCPInstruction.AppendType.LIST) { ListObject input1 = (ListObject) ec.getVariable(inst.input1); - if ( inst.getOpcode().equals("remove")){ + if (inst.getOpcode().equals("remove")) { ScalarObject removePosition = ec.getScalarInput(inst.input2); - PropagatorMultiReturn propagator = new ListRemovePropagator(input1, privacyConstraints[0], removePosition, removePosition.getPrivacyConstraint()); + PropagatorMultiReturn propagator = new ListRemovePropagator(input1, privacyConstraints[0], + removePosition, removePosition.getPrivacyConstraint()); PrivacyConstraint[] outputConstraints = propagator.propagate(); inst.output.setPrivacyConstraint(outputConstraints[0]); ((ListAppendRemoveCPInstruction) inst).getOutput2().setPrivacyConstraint(outputConstraints[1]); } else { ListObject input2 = (ListObject) ec.getVariable(inst.input2); - Propagator propagator = new ListAppendPropagator(input1, privacyConstraints[0], input2, privacyConstraints[1]); + Propagator propagator = new ListAppendPropagator(input1, privacyConstraints[0], input2, + privacyConstraints[1]); inst.output.setPrivacyConstraint(propagator.propagate()); } - } - else { + } else { MatrixBlock input1 = ec.getMatrixInput(inst.input1.getName()); MatrixBlock input2 = ec.getMatrixInput(inst.input2.getName()); Propagator propagator; - if ( inst.getAppendType() == AppendCPInstruction.AppendType.RBIND ) + if (inst.getAppendType() == AppendCPInstruction.AppendType.RBIND) propagator = new RBindPropagator(input1, privacyConstraints[0], input2, privacyConstraints[1]); - else if ( inst.getAppendType() == AppendCPInstruction.AppendType.CBIND ) + else if (inst.getAppendType() == AppendCPInstruction.AppendType.CBIND) propagator = new CBindPropagator(input1, privacyConstraints[0], input2, privacyConstraints[1]); - else throw new DMLPrivacyException("Instruction " + inst.getCPInstructionType() + " with append type " + - inst.getAppendType() + " is not supported by the privacy propagator"); + else + throw new DMLPrivacyException("Instruction " + inst.getCPInstructionType() + " with append type " + + inst.getAppendType() + " is not supported by the privacy propagator"); inst.output.setPrivacyConstraint(propagator.propagate()); ec.releaseMatrixInput(inst.input1.getName(), inst.input2.getName()); } @@ -409,37 +434,44 @@ else throw new DMLPrivacyException("Instruction " + inst.getCPInstructionType() } /** - * Propagates privacy constraints from input to instruction and output CPOperand based on given operator type. + * Propagates privacy constraints from input to instruction and output CPOperand + * based on given operator type. * The propagation is done through the core propagation. - * @param inst instruction for which privacy is propagated - * @param ec execution context + * + * @param inst instruction for which privacy is propagated + * @param ec execution context * @param operatorType defining whether the instruction is aggregating the input - * @return instruction with the merged privacy constraint propagated to it and output CPOperand + * @return instruction with the merged privacy constraint propagated to it and + * output CPOperand */ private static Instruction mergePrivacyConstraintsFromInput(Instruction inst, ExecutionContext ec, - OperatorType operatorType){ - return mergePrivacyConstraintsFromInput(inst, ec, getInputOperands(inst), getOutputOperands(inst), operatorType); + OperatorType operatorType) { + return mergePrivacyConstraintsFromInput(inst, ec, getInputOperands(inst), getOutputOperands(inst), + operatorType); } /** - * Propagates privacy constraints from input to instruction and output CPOperand based on given operator type. + * Propagates privacy constraints from input to instruction and output CPOperand + * based on given operator type. * The propagation is done through the core propagation. - * @param inst instruction for which privacy is propagated - * @param ec execution context - * @param inputs to instruction - * @param outputs of instruction + * + * @param inst instruction for which privacy is propagated + * @param ec execution context + * @param inputs to instruction + * @param outputs of instruction * @param operatorType defining whether the instruction is aggregating the input - * @return instruction with the merged privacy constraint propagated to it and output CPOperand + * @return instruction with the merged privacy constraint propagated to it and + * output CPOperand */ private static Instruction mergePrivacyConstraintsFromInput(Instruction inst, ExecutionContext ec, - CPOperand[] inputs, List outputs, OperatorType operatorType){ - if ( inputs != null && inputs.length > 0 ){ + CPOperand[] inputs, List outputs, OperatorType operatorType) { + if (inputs != null && inputs.length > 0) { PrivacyConstraint[] privacyConstraints = getInputPrivacyConstraints(ec, inputs); - if ( privacyConstraints != null ){ + if (privacyConstraints != null) { PrivacyConstraint mergedPrivacyConstraint = mergeNary(privacyConstraints, operatorType); inst.setPrivacyConstraint(mergedPrivacyConstraint); - for ( CPOperand output : outputs ){ - if ( output != null ) { + for (CPOperand output : outputs) { + if (output != null) { output.setPrivacyConstraint(mergedPrivacyConstraint); } } @@ -449,70 +481,78 @@ private static Instruction mergePrivacyConstraintsFromInput(Instruction inst, Ex } /** - * Throw exception if privacy constraint activated for instruction or for input to instruction. + * Throw exception if privacy constraint activated for instruction or for input + * to instruction. + * * @param inst covariance instruction - * @param ec execution context + * @param ec execution context * @return input instruction if privacy constraints are not activated */ - private static Instruction throwExceptionIfInputOrInstPrivacy(Instruction inst, ExecutionContext ec){ + private static Instruction throwExceptionIfInputOrInstPrivacy(Instruction inst, ExecutionContext ec) { throwExceptionIfPrivacyActivated(inst); CPOperand[] inputOperands = getInputOperands(inst); - if (inputOperands != null){ - for ( CPOperand input : inputOperands ){ + if (inputOperands != null) { + for (CPOperand input : inputOperands) { PrivacyConstraint privacyConstraint = getInputPrivacyConstraint(ec, input); - if ( privacyConstraint != null && privacyConstraint.hasConstraints()){ - throw new DMLPrivacyException("Input of instruction " + inst + " has privacy constraints activated, but the constraints are not propagated during preprocessing of instruction."); + if (privacyConstraint != null && privacyConstraint.hasConstraints()) { + throw new DMLPrivacyException("Input of instruction " + inst + + " has privacy constraints activated, but the constraints are not propagated during preprocessing of instruction."); } } } return inst; } - private static void throwExceptionIfPrivacyActivated(Instruction inst){ - if ( inst.getPrivacyConstraint() != null && inst.getPrivacyConstraint().hasConstraints() ) { - throw new DMLPrivacyException("Instruction " + inst + " has privacy constraints activated, but the constraints are not propagated during preprocessing of instruction."); + private static void throwExceptionIfPrivacyActivated(Instruction inst) { + if (inst.getPrivacyConstraint() != null && inst.getPrivacyConstraint().hasConstraints()) { + throw new DMLPrivacyException("Instruction " + inst + + " has privacy constraints activated, but the constraints are not propagated during preprocessing of instruction."); } } /** * Propagate privacy constraint to instruction and output of instruction - * if data of first input is CacheableData and + * if data of first input is CacheableData and * privacy constraint is activated. + * * @param inst VariableCPInstruction - * @param ec execution context + * @param ec execution context * @return instruction with or without privacy constraints */ - private static Instruction propagateFirstInputPrivacy(VariableCPInstruction inst, ExecutionContext ec){ + private static Instruction propagateFirstInputPrivacy(VariableCPInstruction inst, ExecutionContext ec) { return propagateInputPrivacy(inst, ec, inst.getInput1(), inst.getOutput()); } /** * Propagate privacy constraint to instruction and output of instruction - * if data of second input is CacheableData and + * if data of second input is CacheableData and * privacy constraint is activated. + * * @param inst VariableCPInstruction - * @param ec execution context + * @param ec execution context * @return instruction with or without privacy constraints */ - private static Instruction propagateSecondInputPrivacy(VariableCPInstruction inst, ExecutionContext ec){ + private static Instruction propagateSecondInputPrivacy(VariableCPInstruction inst, ExecutionContext ec) { return propagateInputPrivacy(inst, ec, inst.getInput2(), inst.getOutput()); } /** * Propagate privacy constraint to instruction and output of instruction - * if data of the specified variable is CacheableData + * if data of the specified variable is CacheableData * and privacy constraint is activated - * @param inst instruction - * @param ec execution context - * @param inputOperand input from which the privacy constraint is found + * + * @param inst instruction + * @param ec execution context + * @param inputOperand input from which the privacy constraint is found * @param outputOperand output which the privacy constraint is propagated to * @return instruction with or without privacy constraints */ - private static Instruction propagateInputPrivacy(Instruction inst, ExecutionContext ec, CPOperand inputOperand, CPOperand outputOperand){ + private static Instruction propagateInputPrivacy(Instruction inst, ExecutionContext ec, CPOperand inputOperand, + CPOperand outputOperand) { PrivacyConstraint privacyConstraint = getInputPrivacyConstraint(ec, inputOperand); - if ( privacyConstraint != null ) { + if (privacyConstraint != null) { inst.setPrivacyConstraint(privacyConstraint); - if ( outputOperand != null) + if (outputOperand != null) outputOperand.setPrivacyConstraint(privacyConstraint); } return inst; @@ -520,51 +560,59 @@ private static Instruction propagateInputPrivacy(Instruction inst, ExecutionCont /** * Get privacy constraint of input data variable from execution context. - * @param ec execution context from which the data variable is retrieved + * + * @param ec execution context from which the data variable is retrieved * @param input data variable from which the privacy constraint is retrieved - * @return privacy constraint of variable or null if privacy constraint is not set + * @return privacy constraint of variable or null if privacy constraint is not + * set */ - private static PrivacyConstraint getInputPrivacyConstraint(ExecutionContext ec, CPOperand input){ - if ( input != null && input.getName() != null){ + private static PrivacyConstraint getInputPrivacyConstraint(ExecutionContext ec, CPOperand input) { + if (input != null && input.getName() != null) { Data dd = ec.getVariable(input.getName()); - if ( dd != null ) + if (dd != null) return dd.getPrivacyConstraint(); } return null; } /** - * Returns input privacy constraints as array or returns null if no privacy constraints are found in the inputs. - * @param ec execution context + * Returns input privacy constraints as array or returns null if no privacy + * constraints are found in the inputs. + * + * @param ec execution context * @param inputs from which privacy constraints are retrieved * @return array of privacy constraints from inputs */ - private static PrivacyConstraint[] getInputPrivacyConstraints(ExecutionContext ec, CPOperand[] inputs){ - if ( inputs != null && inputs.length > 0){ + private static PrivacyConstraint[] getInputPrivacyConstraints(ExecutionContext ec, CPOperand[] inputs) { + if (inputs != null && inputs.length > 0) { boolean privacyFound = false; PrivacyConstraint[] privacyConstraints = new PrivacyConstraint[inputs.length]; - for ( int i = 0; i < inputs.length; i++ ){ + for (int i = 0; i < inputs.length; i++) { privacyConstraints[i] = getInputPrivacyConstraint(ec, inputs[i]); - if ( privacyConstraints[i] != null ) + if (privacyConstraints[i] != null) privacyFound = true; } - if ( privacyFound ) + if (privacyFound) return privacyConstraints; } return null; } /** - * Set privacy constraint of data variable with outputName + * Set privacy constraint of data variable with outputName * if the variable exists and the privacy constraint is not null. - * @param ec execution context from which the data variable is retrieved + * + * @param ec execution context from which the data variable is + * retrieved * @param privacyConstraint privacy constraint which the variable should have - * @param outputName name of variable that is retrieved from the execution context + * @param outputName name of variable that is retrieved from the + * execution context */ - private static void setOutputPrivacyConstraint(ExecutionContext ec, PrivacyConstraint privacyConstraint, String outputName){ - if ( privacyConstraint != null ){ + private static void setOutputPrivacyConstraint(ExecutionContext ec, PrivacyConstraint privacyConstraint, + String outputName) { + if (privacyConstraint != null) { Data dd = ec.getVariable(outputName); - if ( dd != null ){ + if (dd != null) { dd.setPrivacyConstraints(privacyConstraint); ec.setVariable(outputName, dd); } @@ -572,48 +620,54 @@ private static void setOutputPrivacyConstraint(ExecutionContext ec, PrivacyConst } /** - * Returns input CPOperands of instruction or returns null if instruction type is not supported by this method. + * Returns input CPOperands of instruction or returns null if instruction type + * is not supported by this method. + * * @param inst instruction from which the inputs are retrieved * @return array of input CPOperands or null */ - private static CPOperand[] getInputOperands(Instruction inst){ - if ( inst instanceof ComputationCPInstruction ) - return ((ComputationCPInstruction)inst).getInputs(); - if ( inst instanceof BuiltinNaryCPInstruction ) - return ((BuiltinNaryCPInstruction)inst).getInputs(); - if ( inst instanceof FunctionCallCPInstruction ) - return ((FunctionCallCPInstruction)inst).getInputs(); - if ( inst instanceof SqlCPInstruction ) - return ((SqlCPInstruction)inst).getInputs(); - else return null; + private static CPOperand[] getInputOperands(Instruction inst) { + if (inst instanceof ComputationCPInstruction) + return ((ComputationCPInstruction) inst).getInputs(); + if (inst instanceof BuiltinNaryCPInstruction) + return ((BuiltinNaryCPInstruction) inst).getInputs(); + if (inst instanceof FunctionCallCPInstruction) + return ((FunctionCallCPInstruction) inst).getInputs(); + if (inst instanceof SqlCPInstruction) + return ((SqlCPInstruction) inst).getInputs(); + else + return null; } /** - * Returns a list of output CPOperands of instruction or an empty list if the instruction has no outputs. - * Note that this method needs to be extended as new instruction types are added, otherwise it will + * Returns a list of output CPOperands of instruction or an empty list if the + * instruction has no outputs. + * Note that this method needs to be extended as new instruction types are + * added, otherwise it will * return an empty list for instructions that may have outputs. + * * @param inst instruction from which the outputs are retrieved * @return list of outputs */ - private static List getOutputOperands(Instruction inst){ + private static List getOutputOperands(Instruction inst) { // The order of the following statements is important - if ( inst instanceof MultiReturnParameterizedBuiltinCPInstruction ) + if (inst instanceof MultiReturnParameterizedBuiltinCPInstruction) return ((MultiReturnParameterizedBuiltinCPInstruction) inst).getOutputs(); - else if ( inst instanceof MultiReturnBuiltinCPInstruction ) + else if (inst instanceof MultiReturnBuiltinCPInstruction) return ((MultiReturnBuiltinCPInstruction) inst).getOutputs(); - else if ( inst instanceof ComputationCPInstruction ) + else if (inst instanceof ComputationCPInstruction) return getSingletonList(((ComputationCPInstruction) inst).getOutput()); - else if ( inst instanceof VariableCPInstruction ) + else if (inst instanceof VariableCPInstruction) return getSingletonList(((VariableCPInstruction) inst).getOutput()); - else if ( inst instanceof SqlCPInstruction ) + else if (inst instanceof SqlCPInstruction) return getSingletonList(((SqlCPInstruction) inst).getOutput()); - else if ( inst instanceof BuiltinNaryCPInstruction ) - return getSingletonList(((BuiltinNaryCPInstruction)inst).getOutput()); + else if (inst instanceof BuiltinNaryCPInstruction) + return getSingletonList(((BuiltinNaryCPInstruction) inst).getOutput()); return new ArrayList<>(); } - private static List getSingletonList(CPOperand operand){ - if ( operand != null) + private static List getSingletonList(CPOperand operand) { + if (operand != null) return new ArrayList<>(Collections.singletonList(operand)); return new ArrayList<>(); } From 3926eac23c48efc63ec4977ed24783a87ef86f62 Mon Sep 17 00:00:00 2001 From: Frederic Zoepffel Date: Mon, 22 Jan 2024 15:45:49 +0545 Subject: [PATCH 046/133] added one input and two input ifft to dml --- .../instructions/CPInstructionParser.java | 8 +- .../instructions/cp/CPInstruction.java | 2 +- .../cp/MultiReturnBuiltinCPInstruction.java | 9 - ...urnComplexMatrixBuiltinCPInstruction.java} | 49 +++- .../runtime/matrix/data/LibCommonsMath.java | 271 +++++++++--------- .../runtime/matrix/data/LibMatrixFourier.java | 124 ++++---- .../propagation/PrivacyPropagator.java | 2 +- 7 files changed, 235 insertions(+), 230 deletions(-) rename src/main/java/org/apache/sysds/runtime/instructions/cp/{MultiReturnMatrixMatrixBuiltinCPInstruction.java => MultiReturnComplexMatrixBuiltinCPInstruction.java} (67%) diff --git a/src/main/java/org/apache/sysds/runtime/instructions/CPInstructionParser.java b/src/main/java/org/apache/sysds/runtime/instructions/CPInstructionParser.java index aacbe73f7b8..cfc4d18b1f9 100644 --- a/src/main/java/org/apache/sysds/runtime/instructions/CPInstructionParser.java +++ b/src/main/java/org/apache/sysds/runtime/instructions/CPInstructionParser.java @@ -56,7 +56,7 @@ import org.apache.sysds.runtime.instructions.cp.MMChainCPInstruction; import org.apache.sysds.runtime.instructions.cp.MMTSJCPInstruction; import org.apache.sysds.runtime.instructions.cp.MultiReturnBuiltinCPInstruction; -import org.apache.sysds.runtime.instructions.cp.MultiReturnMatrixMatrixBuiltinCPInstruction; +import org.apache.sysds.runtime.instructions.cp.MultiReturnComplexMatrixBuiltinCPInstruction; import org.apache.sysds.runtime.instructions.cp.MultiReturnParameterizedBuiltinCPInstruction; import org.apache.sysds.runtime.instructions.cp.PMMJCPInstruction; import org.apache.sysds.runtime.instructions.cp.ParameterizedBuiltinCPInstruction; @@ -331,7 +331,7 @@ public class CPInstructionParser extends InstructionParser { String2CPInstructionType.put("lu", CPType.MultiReturnBuiltin); String2CPInstructionType.put("eigen", CPType.MultiReturnBuiltin); String2CPInstructionType.put("fft", CPType.MultiReturnBuiltin); - String2CPInstructionType.put("ifft", CPType.MultiReturnMatrixMatrixBuiltin); + String2CPInstructionType.put("ifft", CPType.MultiReturnComplexMatrixBuiltin); String2CPInstructionType.put("svd", CPType.MultiReturnBuiltin); String2CPInstructionType.put("partition", CPType.Partition); @@ -423,8 +423,8 @@ public static CPInstruction parseSingleInstruction(CPType cptype, String str) { case MultiReturnParameterizedBuiltin: return MultiReturnParameterizedBuiltinCPInstruction.parseInstruction(str); - case MultiReturnMatrixMatrixBuiltin: - return MultiReturnMatrixMatrixBuiltinCPInstruction.parseInstruction(str); + case MultiReturnComplexMatrixBuiltin: + return MultiReturnComplexMatrixBuiltinCPInstruction.parseInstruction(str); case MultiReturnBuiltin: return MultiReturnBuiltinCPInstruction.parseInstruction(str); diff --git a/src/main/java/org/apache/sysds/runtime/instructions/cp/CPInstruction.java b/src/main/java/org/apache/sysds/runtime/instructions/cp/CPInstruction.java index 434650d18a4..1847cc0cae6 100644 --- a/src/main/java/org/apache/sysds/runtime/instructions/cp/CPInstruction.java +++ b/src/main/java/org/apache/sysds/runtime/instructions/cp/CPInstruction.java @@ -41,7 +41,7 @@ public abstract class CPInstruction extends Instruction { public enum CPType { AggregateUnary, AggregateBinary, AggregateTernary, Unary, Binary, Ternary, Quaternary, BuiltinNary, Ctable, - MultiReturnParameterizedBuiltin, ParameterizedBuiltin, MultiReturnBuiltin, MultiReturnMatrixMatrixBuiltin, + MultiReturnParameterizedBuiltin, ParameterizedBuiltin, MultiReturnBuiltin, MultiReturnComplexMatrixBuiltin, Builtin, Reorg, Variable, FCall, Append, Rand, QSort, QPick, Local, MatrixIndexing, MMTSJ, PMMJ, MMChain, Reshape, Partition, Compression, DeCompression, SpoofFused, StringInit, CentralMoment, Covariance, UaggOuterChain, Dnn, Sql, Prefetch, Broadcast, TrigRemote, diff --git a/src/main/java/org/apache/sysds/runtime/instructions/cp/MultiReturnBuiltinCPInstruction.java b/src/main/java/org/apache/sysds/runtime/instructions/cp/MultiReturnBuiltinCPInstruction.java index a2497d07a2e..26874cc8708 100644 --- a/src/main/java/org/apache/sysds/runtime/instructions/cp/MultiReturnBuiltinCPInstruction.java +++ b/src/main/java/org/apache/sysds/runtime/instructions/cp/MultiReturnBuiltinCPInstruction.java @@ -95,15 +95,6 @@ public static MultiReturnBuiltinCPInstruction parseInstruction(String str) { return new MultiReturnBuiltinCPInstruction(null, in1, outputs, opcode, str); - // } else if (opcode.equalsIgnoreCase("ifft")) { - // // one input and two outputs - // CPOperand in1 = new CPOperand(parts[1]); - // CPOperand in2 = new CPOperand(parts[2]); - // outputs.add(new CPOperand(parts[3], ValueType.FP64, DataType.MATRIX)); - // outputs.add(new CPOperand(parts[4], ValueType.FP64, DataType.MATRIX)); - - // return new MultiReturnBuiltinCPInstruction(null, in1, in2, outputs, opcode, - // str); } else if (opcode.equalsIgnoreCase("svd")) { CPOperand in1 = new CPOperand(parts[1]); diff --git a/src/main/java/org/apache/sysds/runtime/instructions/cp/MultiReturnMatrixMatrixBuiltinCPInstruction.java b/src/main/java/org/apache/sysds/runtime/instructions/cp/MultiReturnComplexMatrixBuiltinCPInstruction.java similarity index 67% rename from src/main/java/org/apache/sysds/runtime/instructions/cp/MultiReturnMatrixMatrixBuiltinCPInstruction.java rename to src/main/java/org/apache/sysds/runtime/instructions/cp/MultiReturnComplexMatrixBuiltinCPInstruction.java index d4a02173a28..ae045dbc3e5 100644 --- a/src/main/java/org/apache/sysds/runtime/instructions/cp/MultiReturnMatrixMatrixBuiltinCPInstruction.java +++ b/src/main/java/org/apache/sysds/runtime/instructions/cp/MultiReturnComplexMatrixBuiltinCPInstruction.java @@ -34,17 +34,24 @@ import org.apache.sysds.runtime.matrix.data.MatrixBlock; import org.apache.sysds.runtime.matrix.operators.Operator; -public class MultiReturnMatrixMatrixBuiltinCPInstruction extends ComputationCPInstruction { +public class MultiReturnComplexMatrixBuiltinCPInstruction extends ComputationCPInstruction { protected ArrayList _outputs; - private MultiReturnMatrixMatrixBuiltinCPInstruction(Operator op, CPOperand input1, CPOperand input2, + private MultiReturnComplexMatrixBuiltinCPInstruction(Operator op, CPOperand input1, CPOperand input2, ArrayList outputs, String opcode, String istr) { super(CPType.MultiReturnBuiltin, op, input1, input2, outputs.get(0), opcode, istr); _outputs = outputs; } + private MultiReturnComplexMatrixBuiltinCPInstruction(Operator op, CPOperand input1, ArrayList outputs, + String opcode, + String istr) { + super(CPType.MultiReturnBuiltin, op, input1, null, outputs.get(0), opcode, istr); + _outputs = outputs; + } + public CPOperand getOutput(int i) { return _outputs.get(i); } @@ -57,21 +64,30 @@ public String[] getOutputNames() { return _outputs.parallelStream().map(output -> output.getName()).toArray(String[]::new); } - public static MultiReturnMatrixMatrixBuiltinCPInstruction parseInstruction(String str) { + public static MultiReturnComplexMatrixBuiltinCPInstruction parseInstruction(String str) { String[] parts = InstructionUtils.getInstructionPartsWithValueType(str); ArrayList outputs = new ArrayList<>(); // first part is always the opcode String opcode = parts[0]; - if (opcode.equalsIgnoreCase("ifft")) { + if (parts.length == 5 && opcode.equalsIgnoreCase("ifft")) { // one input and two outputs CPOperand in1 = new CPOperand(parts[1]); CPOperand in2 = new CPOperand(parts[2]); outputs.add(new CPOperand(parts[3], ValueType.FP64, DataType.MATRIX)); outputs.add(new CPOperand(parts[4], ValueType.FP64, DataType.MATRIX)); - return new MultiReturnMatrixMatrixBuiltinCPInstruction(null, in1, in2, outputs, opcode, str); - } else { + return new MultiReturnComplexMatrixBuiltinCPInstruction(null, in1, in2, outputs, opcode, str); + } else if (parts.length == 4 && opcode.equalsIgnoreCase("ifft")) { + // one input and two outputs + CPOperand in1 = new CPOperand(parts[1]); + outputs.add(new CPOperand(parts[2], ValueType.FP64, DataType.MATRIX)); + outputs.add(new CPOperand(parts[3], ValueType.FP64, DataType.MATRIX)); + + return new MultiReturnComplexMatrixBuiltinCPInstruction(null, in1, outputs, opcode, str); + } + + { throw new DMLRuntimeException("Invalid opcode in MultiReturnBuiltin instruction: " + opcode); } @@ -83,6 +99,27 @@ public int getNumOutputs() { @Override public void processInstruction(ExecutionContext ec) { + if (input2 == null) + processOneInputInstruction(ec); + else + processTwoInputInstruction(ec); + } + + private void processOneInputInstruction(ExecutionContext ec) { + if (!LibCommonsMath.isSupportedMultiReturnOperation(getOpcode())) + throw new DMLRuntimeException("Invalid opcode in MultiReturnBuiltin instruction: " + getOpcode()); + + MatrixBlock in = ec.getMatrixInput(input1.getName()); + MatrixBlock[] out = LibCommonsMath.multiReturnOperations(in, getOpcode()); + + ec.releaseMatrixInput(input1.getName()); + + for (int i = 0; i < _outputs.size(); i++) { + ec.setMatrixOutput(_outputs.get(i).getName(), out[i]); + } + } + + private void processTwoInputInstruction(ExecutionContext ec) { if (!LibCommonsMath.isSupportedMultiReturnOperation(getOpcode())) throw new DMLRuntimeException("Invalid opcode in MultiReturnBuiltin instruction: " + getOpcode()); diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java index d3491a8f986..924477100d7 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java @@ -53,27 +53,26 @@ import static org.apache.sysds.runtime.matrix.data.LibMatrixFourier.ifft; /** - * Library for matrix operations that need invocation of - * Apache Commons Math library. + * Library for matrix operations that need invocation of + * Apache Commons Math library. * * This library currently supports following operations: - * matrix inverse, matrix decompositions (QR, LU, Eigen), solve + * matrix inverse, matrix decompositions (QR, LU, Eigen), solve */ -public class LibCommonsMath -{ +public class LibCommonsMath { private static final Log LOG = LogFactory.getLog(LibCommonsMath.class.getName()); private static final double RELATIVE_SYMMETRY_THRESHOLD = 1e-6; private static final double EIGEN_LAMBDA = 1e-8; private LibCommonsMath() { - //prevent instantiation via private constructor + // prevent instantiation via private constructor } - - public static boolean isSupportedUnaryOperation( String opcode ) { - return ( opcode.equals("inverse") || opcode.equals("cholesky") ); + + public static boolean isSupportedUnaryOperation(String opcode) { + return (opcode.equals("inverse") || opcode.equals("cholesky")); } - - public static boolean isSupportedMultiReturnOperation( String opcode ) { + + public static boolean isSupportedMultiReturnOperation(String opcode) { switch (opcode) { case "qr": @@ -81,19 +80,21 @@ public static boolean isSupportedMultiReturnOperation( String opcode ) { case "eigen": case "fft": case "ifft": - case "svd": return true; - default: return false; + case "svd": + return true; + default: + return false; } } - - public static boolean isSupportedMatrixMatrixOperation( String opcode ) { - return ( opcode.equals("solve") ); + + public static boolean isSupportedMatrixMatrixOperation(String opcode) { + return (opcode.equals("solve")); } - + public static MatrixBlock unaryOperations(MatrixBlock inj, String opcode) { Array2DRowRealMatrix matrixInput = DataConverter.convertToArray2DRowRealMatrix(inj); - if(opcode.equals("inverse")) + if (opcode.equals("inverse")) return computeMatrixInverse(matrixInput); else if (opcode.equals("cholesky")) return computeCholesky(matrixInput); @@ -133,9 +134,8 @@ public static MatrixBlock[] multiReturnOperations(MatrixBlock in, String opcode, return computeEigenQR(in, threads); case "fft": return computeFFT(in); - // TODO: add ifft for only one input - // case "ifft": - // return computeIFFT(in); + case "ifft": + return computeIFFT(in, null); case "svd": return computeSvd(in); default: @@ -174,24 +174,26 @@ public static MatrixBlock matrixMatrixOperations(MatrixBlock in1, MatrixBlock in * @return matrix block */ private static MatrixBlock computeSolve(MatrixBlock in1, MatrixBlock in2) { - //convert to commons math BlockRealMatrix instead of Array2DRowRealMatrix - //to avoid unnecessary conversion as QR internally creates a BlockRealMatrix + // convert to commons math BlockRealMatrix instead of Array2DRowRealMatrix + // to avoid unnecessary conversion as QR internally creates a BlockRealMatrix BlockRealMatrix matrixInput = DataConverter.convertToBlockRealMatrix(in1); BlockRealMatrix vectorInput = DataConverter.convertToBlockRealMatrix(in2); - - /*LUDecompositionImpl ludecompose = new LUDecompositionImpl(matrixInput); - DecompositionSolver lusolver = ludecompose.getSolver(); - RealMatrix solutionMatrix = lusolver.solve(vectorInput);*/ - + + /* + * LUDecompositionImpl ludecompose = new LUDecompositionImpl(matrixInput); + * DecompositionSolver lusolver = ludecompose.getSolver(); + * RealMatrix solutionMatrix = lusolver.solve(vectorInput); + */ + // Setup a solver based on QR Decomposition QRDecomposition qrdecompose = new QRDecomposition(matrixInput); DecompositionSolver solver = qrdecompose.getSolver(); // Invoke solve RealMatrix solutionMatrix = solver.solve(vectorInput); - + return DataConverter.convertToMatrixBlock(solutionMatrix); } - + /** * Function to perform QR decomposition on a given matrix. * @@ -200,19 +202,19 @@ private static MatrixBlock computeSolve(MatrixBlock in1, MatrixBlock in2) { */ private static MatrixBlock[] computeQR(MatrixBlock in) { Array2DRowRealMatrix matrixInput = DataConverter.convertToArray2DRowRealMatrix(in); - + // Perform QR decomposition QRDecomposition qrdecompose = new QRDecomposition(matrixInput); RealMatrix H = qrdecompose.getH(); RealMatrix R = qrdecompose.getR(); - + // Read the results into native format MatrixBlock mbH = DataConverter.convertToMatrixBlock(H.getData()); MatrixBlock mbR = DataConverter.convertToMatrixBlock(R.getData()); return new MatrixBlock[] { mbH, mbR }; } - + /** * Function to perform LU decomposition on a given matrix. * @@ -220,20 +222,20 @@ private static MatrixBlock[] computeQR(MatrixBlock in) { * @return array of matrix blocks */ private static MatrixBlock[] computeLU(MatrixBlock in) { - if(in.getNumRows() != in.getNumColumns()) { + if (in.getNumRows() != in.getNumColumns()) { throw new DMLRuntimeException( - "LU Decomposition can only be done on a square matrix. Input matrix is rectangular (rows=" - + in.getNumRows() + ", cols=" + in.getNumColumns() + ")"); + "LU Decomposition can only be done on a square matrix. Input matrix is rectangular (rows=" + + in.getNumRows() + ", cols=" + in.getNumColumns() + ")"); } - + Array2DRowRealMatrix matrixInput = DataConverter.convertToArray2DRowRealMatrix(in); - + // Perform LUP decomposition LUDecomposition ludecompose = new LUDecomposition(matrixInput); RealMatrix P = ludecompose.getP(); RealMatrix L = ludecompose.getL(); RealMatrix U = ludecompose.getU(); - + // Read the results into native format MatrixBlock mbP = DataConverter.convertToMatrixBlock(P.getData()); MatrixBlock mbL = DataConverter.convertToMatrixBlock(L.getData()); @@ -241,7 +243,7 @@ private static MatrixBlock[] computeLU(MatrixBlock in) { return new MatrixBlock[] { mbP, mbL, mbU }; } - + /** * Function to perform Eigen decomposition on a given matrix. * Input must be a symmetric matrix. @@ -250,21 +252,20 @@ private static MatrixBlock[] computeLU(MatrixBlock in) { * @return array of matrix blocks */ private static MatrixBlock[] computeEigen(MatrixBlock in) { - if ( in.getNumRows() != in.getNumColumns() ) { + if (in.getNumRows() != in.getNumColumns()) { throw new DMLRuntimeException("Eigen Decomposition can only be done on a square matrix. " - + "Input matrix is rectangular (rows=" + in.getNumRows() + ", cols="+ in.getNumColumns() +")"); + + "Input matrix is rectangular (rows=" + in.getNumRows() + ", cols=" + in.getNumColumns() + ")"); } - + EigenDecomposition eigendecompose = null; try { Array2DRowRealMatrix matrixInput = DataConverter.convertToArray2DRowRealMatrix(in); eigendecompose = new EigenDecomposition(matrixInput); - } - catch(MaxCountExceededException ex) { - LOG.warn("Eigen: "+ ex.getMessage()+". Falling back to regularized eigen factorization."); + } catch (MaxCountExceededException ex) { + LOG.warn("Eigen: " + ex.getMessage() + ". Falling back to regularized eigen factorization."); eigendecompose = computeEigenRegularized(in); } - + RealMatrix eVectorsMatrix = eigendecompose.getV(); double[][] eVectors = eVectorsMatrix.getData(); double[] eValues = eigendecompose.getRealEigenvalues(); @@ -273,24 +274,24 @@ private static MatrixBlock[] computeEigen(MatrixBlock in) { } private static EigenDecomposition computeEigenRegularized(MatrixBlock in) { - if( in == null || in.isEmptyBlock(false) ) + if (in == null || in.isEmptyBlock(false)) throw new DMLRuntimeException("Invalid empty block"); - - //slightly modify input for regularization (pos/neg) + + // slightly modify input for regularization (pos/neg) MatrixBlock in2 = new MatrixBlock(in, false); DenseBlock a = in2.getDenseBlock(); - for( int i=0; i= 1e-7) + if (Math.abs(v1.sumSq() - 1.0) >= 1e-7) throw new DMLRuntimeException("v1 not correctly normalized (maybe try changing the seed)"); return v1; } /** - * Function to perform the Lanczos algorithm and then computes the Eigendecomposition. - * Caution: Lanczos is not numerically stable (see https://en.wikipedia.org/wiki/Lanczos_algorithm) + * Function to perform the Lanczos algorithm and then computes the + * Eigendecomposition. + * Caution: Lanczos is not numerically stable (see + * https://en.wikipedia.org/wiki/Lanczos_algorithm) * Input must be a symmetric (and square) matrix. * - * @param in matrix object + * @param in matrix object * @param threads number of threads - * @param seed seed for the random MatrixBlock generation + * @param seed seed for the random MatrixBlock generation * @return array of matrix blocks */ private static MatrixBlock[] computeEigenLanczos(MatrixBlock in, int threads, long seed) { - if(in.getNumRows() != in.getNumColumns()) { + if (in.getNumRows() != in.getNumColumns()) { throw new DMLRuntimeException( - "Lanczos algorithm and Eigen Decomposition can only be done on a square matrix. " - + "Input matrix is rectangular (rows=" + in.getNumRows() + ", cols=" + in.getNumColumns() + ")"); + "Lanczos algorithm and Eigen Decomposition can only be done on a square matrix. " + + "Input matrix is rectangular (rows=" + in.getNumRows() + ", cols=" + in.getNumColumns() + + ")"); } int m = in.getNumRows(); @@ -455,11 +449,12 @@ private static MatrixBlock[] computeEigenLanczos(MatrixBlock in, int threads, lo ScalarOperator op_div_scalar = new RightScalarOperator(Divide.getDivideFnObject(), 1, threads); MatrixBlock beta = new MatrixBlock(1, 1, 0.0); - for(int i = 0; i < m; i++) { + for (int i = 0; i < m; i++) { v1.putInto(TV, 0, i, false); w1 = in.aggregateBinaryOperations(in, v1, op_mul_agg); - MatrixBlock alpha = w1.aggregateBinaryOperations(v1.reorgOperations(op_t, new MatrixBlock(), 0, 0, m), w1, op_mul_agg); - if(i < m - 1) { + MatrixBlock alpha = w1.aggregateBinaryOperations(v1.reorgOperations(op_t, new MatrixBlock(), 0, 0, m), w1, + op_mul_agg); + if (i < m - 1) { w1 = w1.ternaryOperations(op_minus_mul, v1, alpha, new MatrixBlock()); w1 = w1.ternaryOperations(op_minus_mul, v0, beta, new MatrixBlock()); beta.setValue(0, 0, Math.sqrt(w1.sumSq())); @@ -474,7 +469,7 @@ private static MatrixBlock[] computeEigenLanczos(MatrixBlock in, int threads, lo } MatrixBlock[] e = computeEigen(T); - TV.setNonZeros((long) m*m); + TV.setNonZeros((long) m * m); e[1] = TV.aggregateBinaryOperations(TV, e[1], op_mul_agg); return e; } @@ -482,16 +477,17 @@ private static MatrixBlock[] computeEigenLanczos(MatrixBlock in, int threads, lo /** * Function to perform the QR decomposition. * Input must be a square matrix. - * TODO: use Householder transformation and implicit shifts to further speed up QR decompositions + * TODO: use Householder transformation and implicit shifts to further speed up + * QR decompositions * - * @param in matrix object + * @param in matrix object * @param threads number of threads * @return array of matrix blocks [Q, R] */ private static MatrixBlock[] computeQR2(MatrixBlock in, int threads) { - if(in.getNumRows() != in.getNumColumns()) { + if (in.getNumRows() != in.getNumColumns()) { throw new DMLRuntimeException("QR2 Decomposition can only be done on a square matrix. " - + "Input matrix is rectangular (rows=" + in.getNumRows() + ", cols=" + in.getNumColumns() + ")"); + + "Input matrix is rectangular (rows=" + in.getNumRows() + ", cols=" + in.getNumColumns() + ")"); } int m = in.rlen; @@ -500,7 +496,7 @@ private static MatrixBlock[] computeQR2(MatrixBlock in, int threads) { A_n.copy(in); MatrixBlock Q_n = new MatrixBlock(m, m, true); - for(int i = 0; i < m; i++) { + for (int i = 0; i < m; i++) { Q_n.setValue(i, i, 1.0); } @@ -510,7 +506,7 @@ private static MatrixBlock[] computeQR2(MatrixBlock in, int threads) { ScalarOperator op_div_scalar = new RightScalarOperator(Divide.getDivideFnObject(), 1, threads); ScalarOperator op_mult_2 = new LeftScalarOperator(Multiply.getMultiplyFnObject(), 2, threads); - for(int k = 0; k < m; k++) { + for (int k = 0; k < m; k++) { MatrixBlock z = A_n.slice(k, m - 1, k, k); MatrixBlock uk = new MatrixBlock(m - k, 1, 0.0); uk.copy(z); @@ -529,15 +525,16 @@ private static MatrixBlock[] computeQR2(MatrixBlock in, int threads) { Q_n = Q_n.binaryOperations(op_sub, Q_n.aggregateBinaryOperations(Q_n, vkvkt2, op_mul_agg)); } // QR decomp: Q: Q_n; R: A_n - return new MatrixBlock[] {Q_n, A_n}; + return new MatrixBlock[] { Q_n, A_n }; } /** * Function that computes the Eigen Decomposition using the QR algorithm. * Caution: check if the QR algorithm is converged, if not increase iterations - * Caution: if the input matrix has complex eigenvalues results will be incorrect + * Caution: if the input matrix has complex eigenvalues results will be + * incorrect * - * @param in Input matrix + * @param in Input matrix * @param threads number of threads * @return array of matrix blocks */ @@ -546,32 +543,33 @@ private static MatrixBlock[] computeEigenQR(MatrixBlock in, int threads) { } private static MatrixBlock[] computeEigenQR(MatrixBlock in, int num_iterations, double tol, int threads) { - if(in.getNumRows() != in.getNumColumns()) { + if (in.getNumRows() != in.getNumColumns()) { throw new DMLRuntimeException("Eigen Decomposition (QR) can only be done on a square matrix. " - + "Input matrix is rectangular (rows=" + in.getNumRows() + ", cols=" + in.getNumColumns() + ")"); + + "Input matrix is rectangular (rows=" + in.getNumRows() + ", cols=" + in.getNumColumns() + ")"); } int m = in.rlen; AggregateBinaryOperator op_mul_agg = InstructionUtils.getMatMultOperator(threads); MatrixBlock Q_prod = new MatrixBlock(m, m, 0.0); - for(int i = 0; i < m; i++) { + for (int i = 0; i < m; i++) { Q_prod.setValue(i, i, 1.0); } - for(int i = 0; i < num_iterations; i++) { + for (int i = 0; i < num_iterations; i++) { MatrixBlock[] QR = computeQR2(in, threads); Q_prod = Q_prod.aggregateBinaryOperations(Q_prod, QR[0], op_mul_agg); in = QR[1].aggregateBinaryOperations(QR[1], QR[0], op_mul_agg); } - // Is converged if all values are below tol and the there only is values on the diagonal. + // Is converged if all values are below tol and the there only is values on the + // diagonal. double[] check = in.getDenseBlockValues(); double[] eval = new double[m]; - for(int i = 0; i < m; i++) - eval[i] = check[i*m+i]; - + for (int i = 0; i < m; i++) + eval[i] = check[i * m + i]; + double[] evec = Q_prod.getDenseBlockValues(); return sortEVs(eval, evec); } @@ -579,7 +577,7 @@ private static MatrixBlock[] computeEigenQR(MatrixBlock in, int num_iterations, /** * Function to compute the Householder transformation of a Matrix. * - * @param in Input Matrix + * @param in Input Matrix * @param threads number of threads * @return transformed matrix */ @@ -590,14 +588,14 @@ private static MatrixBlock computeHouseholder(MatrixBlock in, int threads) { MatrixBlock A_n = new MatrixBlock(m, m, 0.0); A_n.copy(in); - for(int k = 0; k < m - 2; k++) { + for (int k = 0; k < m - 2; k++) { MatrixBlock ajk = A_n.slice(0, m - 1, k, k); - for(int i = 0; i <= k; i++) { + for (int i = 0; i <= k; i++) { ajk.setValue(i, 0, 0.0); } double alpha = Math.sqrt(ajk.sumSq()); double ak1k = A_n.getDouble(k + 1, k); - if(ak1k > 0.0) + if (ak1k > 0.0) alpha *= -1; double r = Math.sqrt(0.5 * (alpha * alpha - ak1k * alpha)); MatrixBlock v = new MatrixBlock(m, 1, 0.0); @@ -607,7 +605,7 @@ private static MatrixBlock computeHouseholder(MatrixBlock in, int threads) { v = v.scalarOperations(op_div_scalar, new MatrixBlock()); MatrixBlock P = new MatrixBlock(m, m, 0.0); - for(int i = 0; i < m; i++) { + for (int i = 0; i < m; i++) { P.setValue(i, i, 1.0); } @@ -626,7 +624,8 @@ private static MatrixBlock computeHouseholder(MatrixBlock in, int threads) { } /** - * Sort the eigen values (and vectors) in increasing order (to be compatible w/ LAPACK.DSYEVR()) + * Sort the eigen values (and vectors) in increasing order (to be compatible w/ + * LAPACK.DSYEVR()) * * @param eValues Eigenvalues * @param eVectors Eigenvectors @@ -634,19 +633,19 @@ private static MatrixBlock computeHouseholder(MatrixBlock in, int threads) { */ private static MatrixBlock[] sortEVs(double[] eValues, double[][] eVectors) { int n = eValues.length; - for(int i = 0; i < n; i++) { + for (int i = 0; i < n; i++) { int k = i; double p = eValues[i]; - for(int j = i + 1; j < n; j++) { - if(eValues[j] < p) { + for (int j = i + 1; j < n; j++) { + if (eValues[j] < p) { k = j; p = eValues[j]; } } - if(k != i) { + if (k != i) { eValues[k] = eValues[i]; eValues[i] = p; - for(int j = 0; j < n; j++) { + for (int j = 0; j < n; j++) { p = eVectors[j][i]; eVectors[j][i] = eVectors[j][k]; eVectors[j][k] = p; @@ -656,27 +655,27 @@ private static MatrixBlock[] sortEVs(double[] eValues, double[][] eVectors) { MatrixBlock eval = DataConverter.convertToMatrixBlock(eValues, true); MatrixBlock evec = DataConverter.convertToMatrixBlock(eVectors); - return new MatrixBlock[] {eval, evec}; + return new MatrixBlock[] { eval, evec }; } private static MatrixBlock[] sortEVs(double[] eValues, double[] eVectors) { int n = eValues.length; - for(int i = 0; i < n; i++) { + for (int i = 0; i < n; i++) { int k = i; double p = eValues[i]; - for(int j = i + 1; j < n; j++) { - if(eValues[j] < p) { + for (int j = i + 1; j < n; j++) { + if (eValues[j] < p) { k = j; p = eValues[j]; } } - if(k != i) { + if (k != i) { eValues[k] = eValues[i]; eValues[i] = p; - for(int j = 0; j < n; j++) { - p = eVectors[j*n+i]; - eVectors[j*n+i] = eVectors[j*n+k]; - eVectors[j*n+k] = p; + for (int j = 0; j < n; j++) { + p = eVectors[j * n + i]; + eVectors[j * n + i] = eVectors[j * n + k]; + eVectors[j * n + k] = p; } } } @@ -684,6 +683,6 @@ private static MatrixBlock[] sortEVs(double[] eValues, double[] eVectors) { MatrixBlock eval = DataConverter.convertToMatrixBlock(eValues, true); MatrixBlock evec = new MatrixBlock(n, n, false); evec.init(eVectors, n, n); - return new MatrixBlock[] {eval, evec}; + return new MatrixBlock[] { eval, evec }; } } diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java index 662313cfd90..2c9fd8339dd 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java @@ -25,135 +25,117 @@ public class LibMatrixFourier { /** * Function to perform FFT on two given matrices. - * The first one represents the real values and the second one the imaginary - * values. - * The output also contains one matrix for the real and one for the imaginary - * values. + * The first one represents the real values and the second one the imaginary values. + * The output also contains one matrix for the real and one for the imaginary values. * * @param re matrix object representing the real values * @param im matrix object representing the imaginary values * @return array of two matrix blocks */ - public static MatrixBlock[] fft(MatrixBlock re, MatrixBlock im) { + public static MatrixBlock[] fft(MatrixBlock re, MatrixBlock im){ int rows = re.getNumRows(); int cols = re.getNumColumns(); - if (!isPowerOfTwo(rows) || !isPowerOfTwo(cols)) - throw new RuntimeException("false dimensions"); + if(!isPowerOfTwo(rows) || !isPowerOfTwo(cols)) throw new RuntimeException("false dimensions"); fft(re.getDenseBlockValues(), im.getDenseBlockValues(), rows, cols); - System.out.println("fft result in java function:"); - System.out.println((new MatrixBlock[] { re, im })[0].toString()); - System.out.println("\n\n"); - return new MatrixBlock[] { re, im }; + return new MatrixBlock[]{re, im}; } /** * Function to perform IFFT on two given matrices. - * The first one represents the real values and the second one the imaginary - * values. - * The output also contains one matrix for the real and one for the imaginary - * values. + * The first one represents the real values and the second one the imaginary values. + * The output also contains one matrix for the real and one for the imaginary values. * * @param re matrix object representing the real values * @param im matrix object representing the imaginary values * @return array of two matrix blocks */ - public static MatrixBlock[] ifft(MatrixBlock re, MatrixBlock im) { + public static MatrixBlock[] ifft(MatrixBlock re, MatrixBlock im){ int rows = re.getNumRows(); int cols = re.getNumColumns(); - if (!isPowerOfTwo(rows) || !isPowerOfTwo(cols)) - throw new RuntimeException("false dimensions"); + if(!isPowerOfTwo(rows) || !isPowerOfTwo(cols)) throw new RuntimeException("false dimensions"); ifft(re.getDenseBlockValues(), im.getDenseBlockValues(), rows, cols); - System.out.println("IFFT result in function"); - System.out.println((new MatrixBlock[] { re, im })[0].toString()); - System.out.println("\n\n"); - - return new MatrixBlock[] { re, im }; + return new MatrixBlock[]{re, im}; } /** * Function to perform FFT on two given double arrays. - * The first one represents the real values and the second one the imaginary - * values. + * The first one represents the real values and the second one the imaginary values. * Both arrays get updated and contain the result. * - * @param re array representing the real values - * @param im array representing the imaginary values + * @param re array representing the real values + * @param im array representing the imaginary values * @param rows number of rows * @param cols number of rows */ public static void fft(double[] re, double[] im, int rows, int cols) { - double[] re_inter = new double[rows * cols]; - double[] im_inter = new double[rows * cols]; + double[] re_inter = new double[rows*cols]; + double[] im_inter = new double[rows*cols]; - for (int i = 0; i < rows; i++) { - fft_one_dim(re, im, re_inter, im_inter, i * cols, (i + 1) * cols, cols, 1); + for(int i = 0; i < rows; i++){ + fft_one_dim(re, im, re_inter, im_inter, i*cols, (i+1)*cols, cols, 1); } - for (int j = 0; j < cols; j++) { - fft_one_dim(re, im, re_inter, im_inter, j, j + rows * cols, rows, cols); + for(int j = 0; j < cols; j++){ + fft_one_dim(re, im, re_inter, im_inter, j, j+rows*cols, rows, cols); } } /** * Function to perform IFFT on two given double arrays. - * The first one represents the real values and the second one the imaginary - * values. + * The first one represents the real values and the second one the imaginary values. * Both arrays get updated and contain the result. * - * @param re array representing the real values - * @param im array representing the imaginary values + * @param re array representing the real values + * @param im array representing the imaginary values * @param rows number of rows * @param cols number of rows */ public static void ifft(double[] re, double[] im, int rows, int cols) { - double[] re_inter = new double[rows * cols]; - double[] im_inter = new double[rows * cols]; + double[] re_inter = new double[rows*cols]; + double[] im_inter = new double[rows*cols]; - for (int j = 0; j < cols; j++) { - ifft_one_dim(re, im, re_inter, im_inter, j, j + rows * cols, rows, cols); + for(int j = 0; j < cols; j++){ + ifft_one_dim(re, im, re_inter, im_inter, j, j+rows*cols, rows, cols); } - for (int i = 0; i < rows; i++) { - ifft_one_dim(re, im, re_inter, im_inter, i * cols, (i + 1) * cols, cols, 1); + for(int i = 0; i < rows; i++){ + ifft_one_dim(re, im, re_inter, im_inter, i*cols, (i+1)*cols, cols, 1); } } - public static void fft_one_dim(double[] re, double[] im, double[] re_inter, double[] im_inter, int start, int stop, - int num, int minStep) { + public static void fft_one_dim(double[] re, double[] im, double[] re_inter, double[] im_inter, int start, int stop, int num, int minStep) { // start inclusive, stop exclusive - if (num == 1) - return; + if(num == 1) return; // even indices - for (int step = minStep * (num / 2), subNum = 2; subNum <= num; step /= 2, subNum *= 2) { + for(int step = minStep*(num/2), subNum = 2; subNum <= num; step/=2, subNum*=2){ - double angle = -2 * FastMath.PI / subNum; + double angle = -2*FastMath.PI/subNum; // use ceil for the main (sub)array - for (int sub = 0; sub < FastMath.ceil(num / (2 * (double) subNum)); sub++) { + for(int sub = 0; sub < FastMath.ceil(num/(2*(double)subNum)); sub++){ - for (int isOdd = 0; isOdd < 2; isOdd++) { + for(int isOdd = 0; isOdd < 2; isOdd++) { // no odd values for main (sub)array - if (isOdd == 1 && subNum == num) - return; + if (isOdd == 1 && subNum == num) return; - int startSub = start + sub * minStep + isOdd * (step / 2); + int startSub = start + sub*minStep + isOdd*(step/2); // first iterates over even indices, then over odd indices - for (int j = startSub, cnt = 0; cnt < subNum / 2; j += 2 * step, cnt++) { + for (int j = startSub, cnt = 0; cnt < subNum / 2; j += 2*step, cnt++) { double omega_pow_re = FastMath.cos(cnt * angle); double omega_pow_im = FastMath.sin(cnt * angle); @@ -164,13 +146,13 @@ public static void fft_one_dim(double[] re, double[] im, double[] re_inter, doub int index = startSub + cnt * step; re_inter[index] = re[j] + m_re; - re_inter[index + (stop - start) / 2] = re[j] - m_re; + re_inter[index + (stop-start)/2] = re[j] - m_re; im_inter[index] = im[j] + m_im; - im_inter[index + (stop - start) / 2] = im[j] - m_im; + im_inter[index + (stop-start)/2] = im[j] - m_im; } - for (int j = startSub; j < startSub + (stop - start); j += step) { + for (int j = startSub; j < startSub + (stop-start); j += step) { re[j] = re_inter[j]; im[j] = im_inter[j]; re_inter[j] = 0; @@ -185,23 +167,21 @@ public static void fft_one_dim(double[] re, double[] im, double[] re_inter, doub } - private static void ifft_one_dim(double[] re, double[] im, double[] re_inter, double[] im_inter, int start, - int stop, int num, int minStep) { + private static void ifft_one_dim(double[] re, double[] im, double[] re_inter, double[] im_inter, int start, int stop, int num, int minStep) { // conjugate input - for (int i = start; i < start + num * minStep; i += minStep) { + for (int i = start; i < start+num*minStep; i+=minStep){ im[i] = -im[i]; } // apply fft - // fft_one_dim_recursive(re, im, re_inter, im_inter, start, step, num, - // subArraySize); + //fft_one_dim_recursive(re, im, re_inter, im_inter, start, step, num, subArraySize); fft_one_dim(re, im, re_inter, im_inter, start, stop, num, minStep); // conjugate and scale result - for (int i = start; i < start + num * minStep; i += minStep) { - re[i] = re[i] / num; - im[i] = -im[i] / num; + for (int i = start; i < start+num*minStep; i+=minStep){ + re[i] = re[i]/num; + im[i] = -im[i]/num; } } @@ -212,7 +192,7 @@ private static void ifft_one_dim(double[] re, double[] im, double[] re_inter, do * @param n integer to check * @return true if n is a power of two, false otherwise */ - public static boolean isPowerOfTwo(int n) { + public static boolean isPowerOfTwo(int n){ return (n != 0) && ((n & (n - 1)) == 0); } @@ -224,9 +204,8 @@ public static boolean isPowerOfTwo(int n) { * @param re matrix object representing the real values * @return array of two matrix blocks */ - public static MatrixBlock[] fft(MatrixBlock re) { - return fft(re, - new MatrixBlock(re.getNumRows(), re.getNumColumns(), new double[re.getNumRows() * re.getNumColumns()])); + public static MatrixBlock[] fft(MatrixBlock re){ + return fft(re, new MatrixBlock(re.getNumRows(),re.getNumColumns(), new double[re.getNumRows()*re.getNumColumns()])); } /** @@ -237,9 +216,8 @@ public static MatrixBlock[] fft(MatrixBlock re) { * @param re matrix object representing the real values * @return array of two matrix blocks */ - public static MatrixBlock[] ifft(MatrixBlock re) { - return ifft(re, - new MatrixBlock(re.getNumRows(), re.getNumColumns(), new double[re.getNumRows() * re.getNumColumns()])); + public static MatrixBlock[] ifft(MatrixBlock re){ + return ifft(re, new MatrixBlock(re.getNumRows(),re.getNumColumns(), new double[re.getNumRows()*re.getNumColumns()])); } } diff --git a/src/main/java/org/apache/sysds/runtime/privacy/propagation/PrivacyPropagator.java b/src/main/java/org/apache/sysds/runtime/privacy/propagation/PrivacyPropagator.java index 26aadd17687..4d2996b7856 100644 --- a/src/main/java/org/apache/sysds/runtime/privacy/propagation/PrivacyPropagator.java +++ b/src/main/java/org/apache/sysds/runtime/privacy/propagation/PrivacyPropagator.java @@ -295,7 +295,7 @@ private static Instruction preprocessCPInstruction(CPInstruction inst, Execution case Ternary: case Unary: case MultiReturnBuiltin: - case MultiReturnMatrixMatrixBuiltin: + case MultiReturnComplexMatrixBuiltin: case MultiReturnParameterizedBuiltin: case MatrixIndexing: return mergePrivacyConstraintsFromInput(inst, ec, OperatorType.NonAggregate); From 9321a32c817d4f99dd9189ce86e625987c2367c5 Mon Sep 17 00:00:00 2001 From: Jessica Priebe Date: Tue, 23 Jan 2024 14:08:35 +0100 Subject: [PATCH 047/133] formatting --- .../org/apache/sysds/hops/FunctionOp.java | 376 +-- .../parser/BuiltinFunctionExpression.java | 807 ++--- .../apache/sysds/parser/DMLTranslator.java | 2508 +++++++-------- .../instructions/CPInstructionParser.java | 16 +- .../instructions/cp/CPInstruction.java | 59 +- .../cp/MultiReturnBuiltinCPInstruction.java | 25 +- ...turnComplexMatrixBuiltinCPInstruction.java | 225 +- .../runtime/matrix/data/LibCommonsMath.java | 210 +- .../runtime/matrix/data/LibMatrixFourier.java | 126 +- .../propagation/PrivacyPropagator.java | 332 +- .../test/component/matrix/FourierTest.java | 251 +- .../component/matrix/FourierTestLarge.java | 2754 ++++++++--------- 12 files changed, 3780 insertions(+), 3909 deletions(-) diff --git a/src/main/java/org/apache/sysds/hops/FunctionOp.java b/src/main/java/org/apache/sysds/hops/FunctionOp.java index 4d439bf1001..26b9cc353d2 100644 --- a/src/main/java/org/apache/sysds/hops/FunctionOp.java +++ b/src/main/java/org/apache/sysds/hops/FunctionOp.java @@ -39,53 +39,53 @@ /** * This FunctionOp represents the call to a DML-bodied or external function. * - * Note: Currently, we support expressions in function arguments along with function calls - * in expressions with single outputs, leaving multiple outputs handling as it is. + * Note: Currently, we support expressions in function arguments along with function calls in expressions with single + * outputs, leaving multiple outputs handling as it is. */ -public class FunctionOp extends Hop -{ - public enum FunctionType{ - DML, - MULTIRETURN_BUILTIN, - UNKNOWN - } - +public class FunctionOp extends Hop { + public enum FunctionType { + DML, MULTIRETURN_BUILTIN, UNKNOWN + } + public static final String OPCODE = "fcall"; - + private FunctionType _type = null; private String _fnamespace = null; private String _fname = null; - private boolean _opt = true; //call to optimized/unoptimized + private boolean _opt = true; // call to optimized/unoptimized private boolean _pseudo = false; - - private String[] _inputNames = null; // A,B in C = foo(A=X, B=Y) + + private String[] _inputNames = null; // A,B in C = foo(A=X, B=Y) private String[] _outputNames = null; // C in C = foo(A=X, B=Y) private ArrayList _outputHops = null; - + private FunctionOp() { - //default constructor for clone + // default constructor for clone } - public FunctionOp(FunctionType type, String fnamespace, String fname, String[] inputNames, List inputs, String[] outputNames, ArrayList outputHops) { + public FunctionOp(FunctionType type, String fnamespace, String fname, String[] inputNames, List inputs, + String[] outputNames, ArrayList outputHops) { this(type, fnamespace, fname, inputNames, inputs, outputNames, false); _outputHops = outputHops; } - public FunctionOp(FunctionType type, String fnamespace, String fname, String[] inputNames, List inputs, String[] outputNames, boolean singleOut) { + + public FunctionOp(FunctionType type, String fnamespace, String fname, String[] inputNames, List inputs, + String[] outputNames, boolean singleOut) { this(type, fnamespace, fname, inputNames, inputs, outputNames, singleOut, false); } - - public FunctionOp(FunctionType type, String fnamespace, String fname, String[] inputNames, List inputs, String[] outputNames, boolean singleOut, boolean pseudo) - { - super(fnamespace + Program.KEY_DELIM + fname, DataType.UNKNOWN, ValueType.UNKNOWN ); - + + public FunctionOp(FunctionType type, String fnamespace, String fname, String[] inputNames, List inputs, + String[] outputNames, boolean singleOut, boolean pseudo) { + super(fnamespace + Program.KEY_DELIM + fname, DataType.UNKNOWN, ValueType.UNKNOWN); + _type = type; _fnamespace = fnamespace; _fname = fname; _inputNames = inputNames; _outputNames = outputNames; _pseudo = pseudo; - - for( Hop in : inputs ) { + + for(Hop in : inputs) { getInput().add(in); in.getParent().add(this); } @@ -93,58 +93,57 @@ public FunctionOp(FunctionType type, String fnamespace, String fname, String[] i /** FunctionOps may have any number of inputs. */ @Override - public void checkArity() {} - + public void checkArity() { + } + public String getFunctionKey() { - return DMLProgram.constructFunctionKey( - getFunctionNamespace(), getFunctionName()); + return DMLProgram.constructFunctionKey(getFunctionNamespace(), getFunctionName()); } - + public String getFunctionNamespace() { return _fnamespace; } - + public String getFunctionName() { return _fname; } - - public void setFunctionName( String fname ) { + + public void setFunctionName(String fname) { _fname = fname; } - - public void setFunctionNamespace( String fnamespace ) { + + public void setFunctionNamespace(String fnamespace) { _fnamespace = fnamespace; } - + public void setInputVariableNames(String[] names) { _inputNames = names; } - + public ArrayList getOutputs() { return _outputHops; } - + public String[] getInputVariableNames() { return _inputNames; } - + public String[] getOutputVariableNames() { return _outputNames; } - + public boolean containsOutput(String varname) { - return Arrays.stream(getOutputVariableNames()) - .anyMatch(outName -> outName.equals(varname)); + return Arrays.stream(getOutputVariableNames()).anyMatch(outName -> outName.equals(varname)); } - + public FunctionType getFunctionType() { return _type; } - + public void setCallOptimized(boolean opt) { _opt = opt; } - + public boolean isPseudoFunctionCall() { return _pseudo; } @@ -155,128 +154,154 @@ public boolean allowsAllExecTypes() { } @Override - public void computeMemEstimate( MemoTable memo ) - { - //overwrites default hops behavior - - if( _type == FunctionType.DML ) - _memEstimate = 1; //minimal mem estimate - else if( _type == FunctionType.UNKNOWN ) + public void computeMemEstimate(MemoTable memo) { + // overwrites default hops behavior + + if(_type == FunctionType.DML) + _memEstimate = 1; // minimal mem estimate + else if(_type == FunctionType.UNKNOWN) _memEstimate = CostEstimatorHops.DEFAULT_MEM_SP; - else if ( _type == FunctionType.MULTIRETURN_BUILTIN ) { + else if(_type == FunctionType.MULTIRETURN_BUILTIN) { boolean outputDimsKnown = true; - for(Hop out : getOutputs()){ + for(Hop out : getOutputs()) { outputDimsKnown &= out.dimsKnown(); } - if( outputDimsKnown ) { - long lnnz = (getNnz()>=0)?getNnz():getLength(); + if(outputDimsKnown) { + long lnnz = (getNnz() >= 0) ? getNnz() : getLength(); _outputMemEstimate = computeOutputMemEstimate(getDim1(), getDim2(), lnnz); _processingMemEstimate = computeIntermediateMemEstimate(getDim1(), getDim2(), lnnz); } _memEstimate = getInputOutputSize(); } } - + @Override - protected double computeOutputMemEstimate( long dim1, long dim2, long nnz ) - { - if ( getFunctionType() != FunctionType.MULTIRETURN_BUILTIN ) + protected double computeOutputMemEstimate(long dim1, long dim2, long nnz) { + if(getFunctionType() != FunctionType.MULTIRETURN_BUILTIN) throw new RuntimeException("Invalid call of computeOutputMemEstimate in FunctionOp."); else { - if ( getFunctionName().equalsIgnoreCase("qr") ) { + if(getFunctionName().equalsIgnoreCase("qr")) { // upper-triangular and lower-triangular matrices - long outputH = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(0).getDim1(), getOutputs().get(0).getDim2(), 0.5); - long outputR = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(1).getDim1(), getOutputs().get(1).getDim2(), 0.5); - return outputH+outputR; + long outputH = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(0).getDim1(), + getOutputs().get(0).getDim2(), 0.5); + long outputR = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(1).getDim1(), + getOutputs().get(1).getDim2(), 0.5); + return outputH + outputR; } - else if ( getFunctionName().equalsIgnoreCase("lu") ) { + else if(getFunctionName().equalsIgnoreCase("lu")) { // upper-triangular and lower-triangular matrices - long outputP = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(1).getDim1(), getOutputs().get(1).getDim2(), 1.0/getOutputs().get(1).getDim2()); - long outputL = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(0).getDim1(), getOutputs().get(0).getDim2(), 0.5); - long outputU = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(1).getDim1(), getOutputs().get(1).getDim2(), 0.5); - return outputL+outputU+outputP; + long outputP = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(1).getDim1(), + getOutputs().get(1).getDim2(), 1.0 / getOutputs().get(1).getDim2()); + long outputL = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(0).getDim1(), + getOutputs().get(0).getDim2(), 0.5); + long outputU = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(1).getDim1(), + getOutputs().get(1).getDim2(), 0.5); + return outputL + outputU + outputP; } - else if ( getFunctionName().equalsIgnoreCase("eigen") ) { - long outputVectors = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(0).getDim1(), getOutputs().get(0).getDim2(), 1.0); + else if(getFunctionName().equalsIgnoreCase("eigen")) { + long outputVectors = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(0).getDim1(), + getOutputs().get(0).getDim2(), 1.0); long outputValues = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(1).getDim1(), 1, 1.0); - return outputVectors+outputValues; + return outputVectors + outputValues; } - else if ( getFunctionName().equalsIgnoreCase("fft") ) { - long outputRe = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(0).getDim1(), getOutputs().get(0).getDim2(), 1.0); - long outputIm = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(1).getDim1(), getOutputs().get(1).getDim2(), 1.0); - return outputRe+outputIm; + else if(getFunctionName().equalsIgnoreCase("fft")) { + long outputRe = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(0).getDim1(), + getOutputs().get(0).getDim2(), 1.0); + long outputIm = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(1).getDim1(), + getOutputs().get(1).getDim2(), 1.0); + return outputRe + outputIm; } - else if ( getFunctionName().equalsIgnoreCase("ifft") ) { - long outputRe = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(0).getDim1(), getOutputs().get(0).getDim2(), 1.0); - long outputIm = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(1).getDim1(), getOutputs().get(1).getDim2(), 1.0); - return outputRe+outputIm; + else if(getFunctionName().equalsIgnoreCase("ifft")) { + long outputRe = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(0).getDim1(), + getOutputs().get(0).getDim2(), 1.0); + long outputIm = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(1).getDim1(), + getOutputs().get(1).getDim2(), 1.0); + return outputRe + outputIm; } - else if ( getFunctionName().equalsIgnoreCase("lstm") || getFunctionName().equalsIgnoreCase("lstm_backward") ) { + else if(getFunctionName().equalsIgnoreCase("lstm") || getFunctionName().equalsIgnoreCase("lstm_backward")) { // TODO: To allow for initial version to always run on the GPU - return 0; + return 0; } - else if ( getFunctionName().equalsIgnoreCase("batch_norm2d") || getFunctionName().equalsIgnoreCase("batch_norm2d_train")) { - return OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(0).getDim1(), getOutputs().get(0).getDim2(), 1.0) + - OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(1).getDim1(), getOutputs().get(1).getDim2(), 1.0) + - OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(2).getDim1(), getOutputs().get(2).getDim2(), 1.0) + - OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(3).getDim1(), getOutputs().get(3).getDim2(), 1.0) + - OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(4).getDim1(), getOutputs().get(4).getDim2(), 1.0); + else if(getFunctionName().equalsIgnoreCase("batch_norm2d") || + getFunctionName().equalsIgnoreCase("batch_norm2d_train")) { + return OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(0).getDim1(), + getOutputs().get(0).getDim2(), 1.0) + + OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(1).getDim1(), + getOutputs().get(1).getDim2(), 1.0) + + OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(2).getDim1(), + getOutputs().get(2).getDim2(), 1.0) + + OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(3).getDim1(), + getOutputs().get(3).getDim2(), 1.0) + + OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(4).getDim1(), + getOutputs().get(4).getDim2(), 1.0); } - else if ( getFunctionName().equalsIgnoreCase("batch_norm2d_test") ) { - return OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(0).getDim1(), getOutputs().get(0).getDim2(), 1.0); - } - else if ( getFunctionName().equalsIgnoreCase("batch_norm2d_backward") ) { - return OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(0).getDim1(), getOutputs().get(0).getDim2(), 1.0) + - OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(1).getDim1(), getOutputs().get(1).getDim2(), 1.0) + - OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(2).getDim1(), getOutputs().get(2).getDim2(), 1.0); + else if(getFunctionName().equalsIgnoreCase("batch_norm2d_test")) { + return OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(0).getDim1(), + getOutputs().get(0).getDim2(), 1.0); } - else if ( getFunctionName().equalsIgnoreCase("svd") ) { - long outputU = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(0).getDim1(), getOutputs().get(0).getDim2(), 1.0); - long outputSigma = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(1).getDim1(), getOutputs().get(1).getDim2(), 1.0); - long outputV = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(2).getDim1(), getOutputs().get(2).getDim2(), 1.0); - return outputU+outputSigma+outputV; + else if(getFunctionName().equalsIgnoreCase("batch_norm2d_backward")) { + return OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(0).getDim1(), + getOutputs().get(0).getDim2(), 1.0) + + OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(1).getDim1(), + getOutputs().get(1).getDim2(), 1.0) + + OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(2).getDim1(), + getOutputs().get(2).getDim2(), 1.0); + } + else if(getFunctionName().equalsIgnoreCase("svd")) { + long outputU = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(0).getDim1(), + getOutputs().get(0).getDim2(), 1.0); + long outputSigma = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(1).getDim1(), + getOutputs().get(1).getDim2(), 1.0); + long outputV = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(2).getDim1(), + getOutputs().get(2).getDim2(), 1.0); + return outputU + outputSigma + outputV; } else throw new RuntimeException("Invalid call of computeOutputMemEstimate in FunctionOp."); } } - + @Override - protected double computeIntermediateMemEstimate( long dim1, long dim2, long nnz ) - { - if ( getFunctionType() != FunctionType.MULTIRETURN_BUILTIN ) + protected double computeIntermediateMemEstimate(long dim1, long dim2, long nnz) { + if(getFunctionType() != FunctionType.MULTIRETURN_BUILTIN) throw new RuntimeException("Invalid call of computeIntermediateMemEstimate in FunctionOp."); else { - if ( getFunctionName().equalsIgnoreCase("qr") ) { + if(getFunctionName().equalsIgnoreCase("qr")) { // matrix of size same as the input - return OptimizerUtils.estimateSizeExactSparsity(getInput().get(0).getDim1(), getInput().get(0).getDim2(), 1.0); + return OptimizerUtils.estimateSizeExactSparsity(getInput().get(0).getDim1(), + getInput().get(0).getDim2(), 1.0); } - else if ( getFunctionName().equalsIgnoreCase("lu")) { - // 1D vector - return OptimizerUtils.estimateSizeExactSparsity(getInput().get(0).getDim1(), 1, 1.0); + else if(getFunctionName().equalsIgnoreCase("lu")) { + // 1D vector + return OptimizerUtils.estimateSizeExactSparsity(getInput().get(0).getDim1(), 1, 1.0); } - else if ( getFunctionName().equalsIgnoreCase("eigen")) { + else if(getFunctionName().equalsIgnoreCase("eigen")) { // One matrix of size original input and three 1D vectors (used to represent tridiagonal matrix) - return OptimizerUtils.estimateSizeExactSparsity(getInput().get(0).getDim1(), getInput().get(0).getDim2(), 1.0) - + 3*OptimizerUtils.estimateSizeExactSparsity(getInput().get(0).getDim1(), 1, 1.0); + return OptimizerUtils.estimateSizeExactSparsity(getInput().get(0).getDim1(), + getInput().get(0).getDim2(), 1.0) + + 3 * OptimizerUtils.estimateSizeExactSparsity(getInput().get(0).getDim1(), 1, 1.0); } - else if ( getFunctionName().equalsIgnoreCase("fft") ) { + else if(getFunctionName().equalsIgnoreCase("fft")) { // 2 matrices of size same as the input - return 2*OptimizerUtils.estimateSizeExactSparsity(getInput().get(0).getDim1(), getInput().get(0).getDim2(), 1.0); + return 2 * OptimizerUtils.estimateSizeExactSparsity(getInput().get(0).getDim1(), + getInput().get(0).getDim2(), 1.0); } - else if ( getFunctionName().equalsIgnoreCase("ifft") ) { + else if(getFunctionName().equalsIgnoreCase("ifft")) { // 2 matrices of size same as the input - return 2*OptimizerUtils.estimateSizeExactSparsity(getInput().get(0).getDim1(), getInput().get(0).getDim2(), 1.0); + return 2 * OptimizerUtils.estimateSizeExactSparsity(getInput().get(0).getDim1(), + getInput().get(0).getDim2(), 1.0); } - else if (getFunctionName().equalsIgnoreCase("batch_norm2d") || getFunctionName().equalsIgnoreCase("batch_norm2d_backward") || - getFunctionName().equalsIgnoreCase("batch_norm2d_train") || getFunctionName().equalsIgnoreCase("batch_norm2d_test")) { - return 0; + else if(getFunctionName().equalsIgnoreCase("batch_norm2d") || + getFunctionName().equalsIgnoreCase("batch_norm2d_backward") || + getFunctionName().equalsIgnoreCase("batch_norm2d_train") || + getFunctionName().equalsIgnoreCase("batch_norm2d_test")) { + return 0; } - else if ( getFunctionName().equalsIgnoreCase("lstm") || getFunctionName().equalsIgnoreCase("lstm_backward") ) { + else if(getFunctionName().equalsIgnoreCase("lstm") || getFunctionName().equalsIgnoreCase("lstm_backward")) { // TODO: To allow for initial version to always run on the GPU - return 0; + return 0; } - else if ( getFunctionName().equalsIgnoreCase("svd")) { + else if(getFunctionName().equalsIgnoreCase("svd")) { double interOutput = OptimizerUtils.estimateSizeExactSparsity(1, getInput().get(0).getDim2(), 1.0); return interOutput; } @@ -284,53 +309,56 @@ else if ( getFunctionName().equalsIgnoreCase("svd")) { throw new RuntimeException("Invalid call of computeIntermediateMemEstimate in FunctionOp."); } } - + @Override - protected DataCharacteristics inferOutputCharacteristics( MemoTable memo ) { + protected DataCharacteristics inferOutputCharacteristics(MemoTable memo) { throw new RuntimeException("Invalid call of inferOutputCharacteristics in FunctionOp."); } - + @Override public boolean isGPUEnabled() { - if(getFunctionName().equalsIgnoreCase("lstm") || getFunctionName().equalsIgnoreCase("lstm_backward") || - getFunctionName().equalsIgnoreCase("batch_norm2d") || getFunctionName().equalsIgnoreCase("batch_norm2d_backward") || - getFunctionName().equalsIgnoreCase("batch_norm2d_train") || getFunctionName().equalsIgnoreCase("batch_norm2d_test")) + if(getFunctionName().equalsIgnoreCase("lstm") || getFunctionName().equalsIgnoreCase("lstm_backward") || + getFunctionName().equalsIgnoreCase("batch_norm2d") || + getFunctionName().equalsIgnoreCase("batch_norm2d_backward") || + getFunctionName().equalsIgnoreCase("batch_norm2d_train") || + getFunctionName().equalsIgnoreCase("batch_norm2d_test")) return true; else return false; } - + @Override - public Lop constructLops() - { - //return already created lops - if( getLops() != null ) + public Lop constructLops() { + // return already created lops + if(getLops() != null) return getLops(); - + ExecType et = optFindExecType(); - - //construct input lops (recursive) + + // construct input lops (recursive) ArrayList tmp = new ArrayList<>(); - for( Hop in : getInput() ) - tmp.add( in.constructLops() ); - - //construct function call - FunctionCallCP fcall = new FunctionCallCP(tmp, _fnamespace, _fname, _inputNames, _outputNames, _outputHops, _opt, et); + for(Hop in : getInput()) + tmp.add(in.constructLops()); + + // construct function call + FunctionCallCP fcall = new FunctionCallCP(tmp, _fnamespace, _fname, _inputNames, _outputNames, _outputHops, + _opt, et); setLineNumbers(fcall); setLops(fcall); - - //note: no reblock lop because outputs directly bound + + // note: no reblock lop because outputs directly bound constructAndSetCompressionLopFunctionalIfRequired(et); return getLops(); } protected void constructAndSetCompressionLopFunctionalIfRequired(ExecType et) { - if((requiresCompression()) && ((FunctionCallCP) getLops()).getFunctionName().equalsIgnoreCase("transformencode")){ // xor - + if((requiresCompression()) && + ((FunctionCallCP) getLops()).getFunctionName().equalsIgnoreCase("transformencode")) { // xor + // Lop matrixOut = lop.getFunctionOutputs().get(0); Lop compressionInstruction = null; - + if(_compressedWorkloadTree != null) { SingletonLookupHashMap m = SingletonLookupHashMap.getMap(); int singletonID = m.put(_compressedWorkloadTree); @@ -338,40 +366,38 @@ protected void constructAndSetCompressionLopFunctionalIfRequired(ExecType et) { } else compressionInstruction = new Compression(getLops(), DataType.MATRIX, ValueType.FP64, et, 0); - - setOutputDimensions( compressionInstruction ); - setLineNumbers( compressionInstruction ); - setLops( compressionInstruction ); + setOutputDimensions(compressionInstruction); + setLineNumbers(compressionInstruction); + setLops(compressionInstruction); } } - @Override public String getOpString() { - return OPCODE + " " + _fnamespace + " " + _fname; + return OPCODE + " " + _fnamespace + " " + _fname; } @Override - protected ExecType optFindExecType(boolean transitive) - { + protected ExecType optFindExecType(boolean transitive) { checkAndSetForcedPlatform(); - - if ( getFunctionType() == FunctionType.MULTIRETURN_BUILTIN ) { + + if(getFunctionType() == FunctionType.MULTIRETURN_BUILTIN) { boolean isBuiltinFunction = isBuiltinFunction(); // check if there is sufficient memory to execute this function - if(isBuiltinFunction && getFunctionName().equalsIgnoreCase("transformencode") ) { - _etype = ((_etypeForced==ExecType.SPARK - || (getMemEstimate() >= OptimizerUtils.getLocalMemBudget() - && OptimizerUtils.isSparkExecutionMode())) ? ExecType.SPARK : ExecType.CP); + if(isBuiltinFunction && getFunctionName().equalsIgnoreCase("transformencode")) { + _etype = ((_etypeForced == ExecType.SPARK || (getMemEstimate() >= OptimizerUtils.getLocalMemBudget() && + OptimizerUtils.isSparkExecutionMode())) ? ExecType.SPARK : ExecType.CP); } - else if(isBuiltinFunction && (getFunctionName().equalsIgnoreCase("lstm") || getFunctionName().equalsIgnoreCase("lstm_backward"))) { + else if(isBuiltinFunction && + (getFunctionName().equalsIgnoreCase("lstm") || getFunctionName().equalsIgnoreCase("lstm_backward"))) { if(!DMLScript.USE_ACCELERATOR) throw new RuntimeException("The function " + getFunctionName() + " is only supported on GPU."); _etype = ExecType.GPU; } - else if(isBuiltinFunction && (getFunctionName().equalsIgnoreCase("batch_norm2d") || getFunctionName().equalsIgnoreCase("batch_norm2d_backward"))) { + else if(isBuiltinFunction && (getFunctionName().equalsIgnoreCase("batch_norm2d") || + getFunctionName().equalsIgnoreCase("batch_norm2d_backward"))) { _etype = DMLScript.USE_ACCELERATOR ? ExecType.GPU : ExecType.CP; } else if(isBuiltinFunction && getFunctionName().equalsIgnoreCase("batch_norm2d_train")) { @@ -381,7 +407,7 @@ else if(isBuiltinFunction && getFunctionName().equalsIgnoreCase("batch_norm2d_tr else { // Since the memory estimate is only conservative, do not throw // exception if the estimated memory is larger than the budget - // Nevertheless, memory estimates these functions are useful for + // Nevertheless, memory estimates these functions are useful for // other purposes, such as compiling parfor _etype = ExecType.CP; } @@ -390,40 +416,40 @@ else if(isBuiltinFunction && getFunctionName().equalsIgnoreCase("batch_norm2d_tr // the actual function call is always CP _etype = ExecType.CP; } - + return _etype; } - + private boolean isBuiltinFunction() { return getFunctionNamespace().equals(DMLProgram.INTERNAL_NAMESPACE); } @Override public void refreshSizeInformation() { - //do nothing + // do nothing } - + @Override @SuppressWarnings("unchecked") public Object clone() throws CloneNotSupportedException { FunctionOp ret = new FunctionOp(); - - //copy generic attributes + + // copy generic attributes ret.clone(this, false); - - //copy specific attributes + + // copy specific attributes ret._type = _type; ret._fnamespace = _fnamespace; ret._fname = _fname; ret._opt = _opt; - ret._inputNames = (_inputNames!=null) ? _inputNames.clone() : null; + ret._inputNames = (_inputNames != null) ? _inputNames.clone() : null; ret._outputNames = _outputNames.clone(); - if( _outputHops != null ) + if(_outputHops != null) ret._outputHops = (ArrayList) _outputHops.clone(); - + return ret; } - + @Override public boolean compare(Hop that) { return false; diff --git a/src/main/java/org/apache/sysds/parser/BuiltinFunctionExpression.java b/src/main/java/org/apache/sysds/parser/BuiltinFunctionExpression.java index 074ca5d3ceb..221c1792630 100644 --- a/src/main/java/org/apache/sysds/parser/BuiltinFunctionExpression.java +++ b/src/main/java/org/apache/sysds/parser/BuiltinFunctionExpression.java @@ -45,12 +45,12 @@ public class BuiltinFunctionExpression extends DataIdentifier { private Builtins _opcode; public BuiltinFunctionExpression(ParserRuleContext ctx, Builtins bifop, ArrayList args, - String fname) { + String fname) { _opcode = bifop; setCtxValuesAndFilename(ctx, fname); args = expandDnnArguments(args); _args = new Expression[args.size()]; - for (int i = 0; i < args.size(); i++) { + for(int i = 0; i < args.size(); i++) { _args[i] = args.get(i).getExpr(); } } @@ -58,7 +58,7 @@ public BuiltinFunctionExpression(ParserRuleContext ctx, Builtins bifop, ArrayLis public BuiltinFunctionExpression(Builtins bifop, Expression[] args, ParseInfo parseInfo) { _opcode = bifop; _args = new Expression[args.length]; - for (int i = 0; i < args.length; i++) { + for(int i = 0; i < args.length; i++) { _args[i] = args[i]; } setParseInfo(parseInfo); @@ -67,7 +67,7 @@ public BuiltinFunctionExpression(Builtins bifop, Expression[] args, ParseInfo pa public BuiltinFunctionExpression(ParserRuleContext ctx, Builtins bifop, Expression[] args, String fname) { _opcode = bifop; _args = new Expression[args.length]; - for (int i = 0; i < args.length; i++) { + for(int i = 0; i < args.length; i++) { _args[i] = args[i]; } setCtxValuesAndFilename(ctx, fname); @@ -76,7 +76,7 @@ public BuiltinFunctionExpression(ParserRuleContext ctx, Builtins bifop, Expressi @Override public Expression rewriteExpression(String prefix) { Expression[] newArgs = new Expression[_args.length]; - for (int i = 0; i < _args.length; i++) { + for(int i = 0; i < _args.length; i++) { newArgs[i] = _args[i].rewriteExpression(prefix); } BuiltinFunctionExpression retVal = new BuiltinFunctionExpression(this._opcode, newArgs, this); @@ -88,35 +88,35 @@ public Builtins getOpCode() { } public Expression getFirstExpr() { - return (_args.length >= 1 ? _args[0] : null); + return(_args.length >= 1 ? _args[0] : null); } public Expression getSecondExpr() { - return (_args.length >= 2 ? _args[1] : null); + return(_args.length >= 2 ? _args[1] : null); } public Expression getThirdExpr() { - return (_args.length >= 3 ? _args[2] : null); + return(_args.length >= 3 ? _args[2] : null); } public Expression getFourthExpr() { - return (_args.length >= 4 ? _args[3] : null); + return(_args.length >= 4 ? _args[3] : null); } public Expression getFifthExpr() { - return (_args.length >= 5 ? _args[4] : null); + return(_args.length >= 5 ? _args[4] : null); } public Expression getSixthExpr() { - return (_args.length >= 6 ? _args[5] : null); + return(_args.length >= 6 ? _args[5] : null); } public Expression getSeventhExpr() { - return (_args.length >= 7 ? _args[6] : null); + return(_args.length >= 7 ? _args[6] : null); } public Expression getEighthExpr() { - return (_args.length >= 8 ? _args[7] : null); + return(_args.length >= 8 ? _args[7] : null); } public Expression[] getAllExpr() { @@ -124,21 +124,21 @@ public Expression[] getAllExpr() { } public Expression getExpr(int i) { - return (_args.length > i ? _args[i] : null); + return(_args.length > i ? _args[i] : null); } @Override public void validateExpression(MultiAssignmentStatement stmt, HashMap ids, - HashMap constVars, boolean conditional) { - if (this.getFirstExpr() instanceof FunctionCallIdentifier) { + HashMap constVars, boolean conditional) { + if(this.getFirstExpr() instanceof FunctionCallIdentifier) { raiseValidateError("UDF function call not supported as parameter to built-in function call", false); } this.getFirstExpr().validateExpression(ids, constVars, conditional); Expression[] expr = getAllExpr(); - if (expr != null && expr.length > 1) { - for (int i = 1; i < expr.length; i++) { - if (expr[i] instanceof FunctionCallIdentifier) { + if(expr != null && expr.length > 1) { + for(int i = 1; i < expr.length; i++) { + if(expr[i] instanceof FunctionCallIdentifier) { raiseValidateError("UDF function call not supported as parameter to built-in function call", false); } expr[i].validateExpression(ids, constVars, conditional); @@ -146,13 +146,13 @@ public void validateExpression(MultiAssignmentStatement stmt, HashMap orderDnnParams(ArrayList paramExpression, - int skip) { + int skip) { ArrayList newParams = new ArrayList<>(); - for (int i = 0; i < skip; i++) + for(int i = 0; i < skip; i++) newParams.add(paramExpression.get(i)); - String[] orderedParams = { - "stride1", "stride2", "padding1", "padding2", - "input_shape1", "input_shape2", "input_shape3", "input_shape4", - "filter_shape1", "filter_shape2", "filter_shape3", "filter_shape4" - }; - for (int i = 0; i < orderedParams.length; i++) { + String[] orderedParams = {"stride1", "stride2", "padding1", "padding2", "input_shape1", "input_shape2", + "input_shape3", "input_shape4", "filter_shape1", "filter_shape2", "filter_shape3", "filter_shape4"}; + for(int i = 0; i < orderedParams.length; i++) { boolean found = false; - for (ParameterExpression param : paramExpression) { - if (param.getName() != null && param.getName().equals(orderedParams[i])) { + for(ParameterExpression param : paramExpression) { + if(param.getName() != null && param.getName().equals(orderedParams[i])) { found = true; newParams.add(param); } } - if (!found) { + if(!found) { throw new LanguageException("Incorrect parameters. Expected " + orderedParams[i] + " to be expanded."); } } @@ -543,16 +541,17 @@ private static ArrayList orderDnnParams(ArrayList replaceListParams(ArrayList paramExpression, - String inputVarName, String outputVarName, int startIndex) { + String inputVarName, String outputVarName, int startIndex) { ArrayList newParamExpression = new ArrayList<>(); int i = startIndex; int j = 1; // Assumption: sequential ordering pool_size1, pool_size2 - for (ParameterExpression expr : paramExpression) { - if (expr.getName() != null && expr.getName().equals(inputVarName + j)) { + for(ParameterExpression expr : paramExpression) { + if(expr.getName() != null && expr.getName().equals(inputVarName + j)) { newParamExpression.add(new ParameterExpression(outputVarName + i, expr.getExpr())); i++; j++; - } else { + } + else { newParamExpression.add(expr); } } @@ -560,21 +559,23 @@ private static ArrayList replaceListParams(ArrayList expandListParams(ArrayList paramExpression, - HashSet paramsToExpand) { + HashSet paramsToExpand) { ArrayList newParamExpressions = new ArrayList<>(); - for (ParameterExpression expr : paramExpression) { - if (paramsToExpand.contains(expr.getName())) { - if (expr.getExpr() instanceof ExpressionList) { + for(ParameterExpression expr : paramExpression) { + if(paramsToExpand.contains(expr.getName())) { + if(expr.getExpr() instanceof ExpressionList) { int i = 1; - for (Expression e : ((ExpressionList) expr.getExpr()).getValue()) { + for(Expression e : ((ExpressionList) expr.getExpr()).getValue()) { newParamExpressions.add(new ParameterExpression(expr.getName() + i, e)); i++; } } - } else if (expr.getExpr() instanceof ExpressionList) { - throw new LanguageException("The parameter " + expr.getName() - + " cannot be list or is not supported for the given function"); - } else { + } + else if(expr.getExpr() instanceof ExpressionList) { + throw new LanguageException( + "The parameter " + expr.getName() + " cannot be list or is not supported for the given function"); + } + else { newParamExpressions.add(expr); } } @@ -583,8 +584,8 @@ private static ArrayList expandListParams(ArrayList expandDnnArguments(ArrayList paramExpression) { try { - if (_opcode == Builtins.CONV2D || _opcode == Builtins.CONV2D_BACKWARD_FILTER - || _opcode == Builtins.CONV2D_BACKWARD_DATA) { + if(_opcode == Builtins.CONV2D || _opcode == Builtins.CONV2D_BACKWARD_FILTER || + _opcode == Builtins.CONV2D_BACKWARD_DATA) { HashSet expand = new HashSet<>(); expand.add("input_shape"); expand.add("filter_shape"); @@ -592,8 +593,9 @@ private ArrayList expandDnnArguments(ArrayList expand = new HashSet<>(); expand.add("input_shape"); expand.add("pool_size"); @@ -603,31 +605,30 @@ private ArrayList expandDnnArguments(ArrayList ids, HashMap constVars, - boolean conditional) { - for (int i = 0; i < _args.length; i++) { - if (_args[i] instanceof FunctionCallIdentifier) { + boolean conditional) { + for(int i = 0; i < _args.length; i++) { + if(_args[i] instanceof FunctionCallIdentifier) { raiseValidateError("UDF function call not supported as parameter to built-in function call", false); } _args[i].validateExpression(ids, constVars, conditional); @@ -638,21 +639,21 @@ public void validateExpression(HashMap ids, HashMap ids, HashMap ids, HashMap ids, HashMap ids, HashMap 2) + if(cumSP && id.getDim2() > 2) raiseValidateError("Cumsumprod only supported over two-column matrices", conditional); output.setDataType(DataType.MATRIX); @@ -792,31 +796,28 @@ public void validateExpression(HashMap ids, HashMap ids, HashMap ids, HashMap ids, HashMap ids, HashMap= 0 && m2rlen >= 0 && m1rlen != m2rlen) { - raiseValidateError("inputs to cbind must have same number of rows: input 1 rows: " + - m1rlen + ", input 2 rows: " + m2rlen, conditional, - LanguageErrorCodes.INVALID_PARAMETERS); + if(getOpCode() == Builtins.CBIND) { + if(m1rlen >= 0 && m2rlen >= 0 && m1rlen != m2rlen) { + raiseValidateError( + "inputs to cbind must have same number of rows: input 1 rows: " + m1rlen + + ", input 2 rows: " + m2rlen, + conditional, LanguageErrorCodes.INVALID_PARAMETERS); } appendDim1 = (m2rlen >= 0) ? m2rlen : appendDim1; appendDim2 = (appendDim2 >= 0 && m2clen >= 0) ? appendDim2 + m2clen : -1; - } else if (getOpCode() == Builtins.RBIND) { - if (m1clen >= 0 && m2clen >= 0 && m1clen != m2clen) { + } + else if(getOpCode() == Builtins.RBIND) { + if(m1clen >= 0 && m2clen >= 0 && m1clen != m2clen) { raiseValidateError( - "inputs to rbind must have same number of columns: input 1 columns: " + - m1clen + ", input 2 columns: " + m2clen, - conditional, LanguageErrorCodes.INVALID_PARAMETERS); + "inputs to rbind must have same number of columns: input 1 columns: " + m1clen + + ", input 2 columns: " + m2clen, + conditional, LanguageErrorCodes.INVALID_PARAMETERS); } appendDim1 = (appendDim1 >= 0 && m2rlen >= 0) ? appendDim1 + m2rlen : -1; appendDim2 = (m2clen >= 0) ? m2clen : appendDim2; @@ -982,20 +988,20 @@ else if (getAllExpr().length == 1) { DataType dt2 = getSecondExpr().getOutput().getDataType(); // check input data types - if (dt1 == DataType.SCALAR && dt2 == DataType.SCALAR) { + if(dt1 == DataType.SCALAR && dt2 == DataType.SCALAR) { raiseValidateError("ppred() requires at least one matrix input.", conditional, - LanguageErrorCodes.INVALID_PARAMETERS); + LanguageErrorCodes.INVALID_PARAMETERS); } - if (dt1 == DataType.MATRIX) + if(dt1 == DataType.MATRIX) checkMatrixParam(getFirstExpr()); - if (dt2 == DataType.MATRIX) + if(dt2 == DataType.MATRIX) checkMatrixParam(getSecondExpr()); // check operator - if (getThirdExpr().getOutput().getDataType() != DataType.SCALAR || - getThirdExpr().getOutput().getValueType() != ValueType.STRING) { + if(getThirdExpr().getOutput().getDataType() != DataType.SCALAR || + getThirdExpr().getOutput().getValueType() != ValueType.STRING) { raiseValidateError("Third argument in ppred() is not an operator ", conditional, - LanguageErrorCodes.INVALID_PARAMETERS); + LanguageErrorCodes.INVALID_PARAMETERS); } setBinaryOutputProperties(output); @@ -1023,18 +1029,18 @@ else if (getAllExpr().length == 1) { checkNumParameters(1); checkMatrixParam(getFirstExpr()); output.setDataType(DataType.MATRIX); - if (id.getDim2() != -1) { // type known - if (id.getDim2() == 1) { + if(id.getDim2() != -1) { // type known + if(id.getDim2() == 1) { // diag V2M output.setDimensions(id.getDim1(), id.getDim1()); - } else { - if (id.getDim1() != id.getDim2()) { + } + else { + if(id.getDim1() != id.getDim2()) { raiseValidateError( - "diag can either: (1) create diagonal matrix from (n x 1) matrix, or (2) take diagonal from a square matrix. " - + "Error invoking diag on matrix with dimensions (" - + id.getDim1() + "," + id.getDim2() - + ") in " + this.toString(), - conditional, LanguageErrorCodes.INVALID_PARAMETERS); + "diag can either: (1) create diagonal matrix from (n x 1) matrix, or (2) take diagonal from a square matrix. " + + "Error invoking diag on matrix with dimensions (" + id.getDim1() + "," + + id.getDim2() + ") in " + this.toString(), + conditional, LanguageErrorCodes.INVALID_PARAMETERS); } // diag M2V output.setDimensions(id.getDim1(), 1); @@ -1047,8 +1053,7 @@ else if (getAllExpr().length == 1) { case NCOL: case LENGTH: checkNumParameters(1); - checkDataTypeParam(getFirstExpr(), - DataType.FRAME, DataType.LIST, DataType.MATRIX); + checkDataTypeParam(getFirstExpr(), DataType.FRAME, DataType.LIST, DataType.MATRIX); output.setDataType(DataType.SCALAR); output.setDimensions(0, 0); output.setBlocksize(0); @@ -1056,8 +1061,7 @@ else if (getAllExpr().length == 1) { break; case LINEAGE: checkNumParameters(1); - checkDataTypeParam(getFirstExpr(), - DataType.MATRIX, DataType.FRAME, DataType.LIST); + checkDataTypeParam(getFirstExpr(), DataType.MATRIX, DataType.FRAME, DataType.LIST); output.setDataType(DataType.SCALAR); output.setDimensions(0, 0); output.setBlocksize(0); @@ -1082,14 +1086,8 @@ else if (getAllExpr().length == 1) { case TABLE: /* - * Allowed #of arguments: 2,3,4,5,6 - * table(A,B) - * table(A,B,W) - * table(A,B,1) - * table(A,B,dim1,dim2) - * table(A,B,W,dim1,dim2) - * table(A,B,1,dim1,dim2) - * table(A,B,1,dim1,dim2,TRUE) + * Allowed #of arguments: 2,3,4,5,6 table(A,B) table(A,B,W) table(A,B,1) table(A,B,dim1,dim2) + * table(A,B,W,dim1,dim2) table(A,B,1,dim1,dim2) table(A,B,1,dim1,dim2,TRUE) */ // Check for validity of input arguments, and setup output dimensions @@ -1097,18 +1095,18 @@ else if (getAllExpr().length == 1) { // First input: is always of type MATRIX checkMatrixParam(getFirstExpr()); - if (getSecondExpr() == null) + if(getSecondExpr() == null) raiseValidateError("Invalid number of arguments to table(). " - + "The table() function requires 2, 3, 4, 5, or 6 arguments.", conditional); + + "The table() function requires 2, 3, 4, 5, or 6 arguments.", conditional); // Second input: can be MATRIX or SCALAR // cases: table(A,B) or table(A,1) - if (getSecondExpr().getOutput().getDataType() == DataType.MATRIX) + if(getSecondExpr().getOutput().getDataType() == DataType.MATRIX) checkMatchingDimensions(getFirstExpr(), getSecondExpr()); long outputDim1 = -1, outputDim2 = -1; - switch (_args.length) { + switch(_args.length) { case 2: // nothing to do break; @@ -1117,7 +1115,7 @@ else if (getAllExpr().length == 1) { // case - table w/ weights // - weights specified as a matrix: table(A,B,W) or table(A,1,W) // - weights specified as a scalar: table(A,B,1) or table(A,1,1) - if (getThirdExpr().getOutput().getDataType() == DataType.MATRIX) + if(getThirdExpr().getOutput().getDataType() == DataType.MATRIX) checkMatchingDimensions(getFirstExpr(), getThirdExpr()); break; @@ -1125,25 +1123,25 @@ else if (getAllExpr().length == 1) { // case - table w/ output dimensions: table(A,B,dim1,dim2) or // table(A,1,dim1,dim2) // third and fourth arguments must be scalars - if (getThirdExpr().getOutput().getDataType() != DataType.SCALAR - || _args[3].getOutput().getDataType() != DataType.SCALAR) { + if(getThirdExpr().getOutput().getDataType() != DataType.SCALAR || + _args[3].getOutput().getDataType() != DataType.SCALAR) { raiseValidateError( - "Invalid argument types to table(): output dimensions must be of type scalar: " - + this.toString(), - conditional, LanguageErrorCodes.INVALID_PARAMETERS); - } else { + "Invalid argument types to table(): output dimensions must be of type scalar: " + + this.toString(), + conditional, LanguageErrorCodes.INVALID_PARAMETERS); + } + else { // constant propagation - if (getThirdExpr() instanceof DataIdentifier - && constVars.containsKey(((DataIdentifier) getThirdExpr()).getName()) - && !conditional) + if(getThirdExpr() instanceof DataIdentifier && + constVars.containsKey(((DataIdentifier) getThirdExpr()).getName()) && !conditional) _args[2] = constVars.get(((DataIdentifier) getThirdExpr()).getName()); - if (_args[3] instanceof DataIdentifier - && constVars.containsKey(((DataIdentifier) _args[3]).getName()) && !conditional) + if(_args[3] instanceof DataIdentifier && + constVars.containsKey(((DataIdentifier) _args[3]).getName()) && !conditional) _args[3] = constVars.get(((DataIdentifier) _args[3]).getName()); - if (getThirdExpr().getOutput() instanceof ConstIdentifier) + if(getThirdExpr().getOutput() instanceof ConstIdentifier) outputDim1 = ((ConstIdentifier) getThirdExpr().getOutput()).getLongValue(); - if (_args[3].getOutput() instanceof ConstIdentifier) + if(_args[3].getOutput() instanceof ConstIdentifier) outputDim2 = ((ConstIdentifier) _args[3].getOutput()).getLongValue(); } break; @@ -1154,41 +1152,42 @@ else if (getAllExpr().length == 1) { // - table(A,B,W,dim1,dim2) or table(A,1,W,dim1,dim2) // - table(A,B,1,dim1,dim2) or table(A,1,1,dim1,dim2) - if (getThirdExpr().getOutput().getDataType() == DataType.MATRIX) + if(getThirdExpr().getOutput().getDataType() == DataType.MATRIX) checkMatchingDimensions(getFirstExpr(), getThirdExpr()); // fourth and fifth arguments must be scalars - if (_args[3].getOutput().getDataType() != DataType.SCALAR - || _args[4].getOutput().getDataType() != DataType.SCALAR) { + if(_args[3].getOutput().getDataType() != DataType.SCALAR || + _args[4].getOutput().getDataType() != DataType.SCALAR) { raiseValidateError( - "Invalid argument types to table(): output dimensions must be of type scalar: " - + this.toString(), - conditional, LanguageErrorCodes.INVALID_PARAMETERS); - } else { + "Invalid argument types to table(): output dimensions must be of type scalar: " + + this.toString(), + conditional, LanguageErrorCodes.INVALID_PARAMETERS); + } + else { // constant propagation - if (_args[3] instanceof DataIdentifier - && constVars.containsKey(((DataIdentifier) _args[3]).getName()) && !conditional) + if(_args[3] instanceof DataIdentifier && + constVars.containsKey(((DataIdentifier) _args[3]).getName()) && !conditional) _args[3] = constVars.get(((DataIdentifier) _args[3]).getName()); - if (_args[4] instanceof DataIdentifier - && constVars.containsKey(((DataIdentifier) _args[4]).getName()) && !conditional) + if(_args[4] instanceof DataIdentifier && + constVars.containsKey(((DataIdentifier) _args[4]).getName()) && !conditional) _args[4] = constVars.get(((DataIdentifier) _args[4]).getName()); - if (_args[3].getOutput() instanceof ConstIdentifier) + if(_args[3].getOutput() instanceof ConstIdentifier) outputDim1 = ((ConstIdentifier) _args[3].getOutput()).getLongValue(); - if (_args[4].getOutput() instanceof ConstIdentifier) + if(_args[4].getOutput() instanceof ConstIdentifier) outputDim2 = ((ConstIdentifier) _args[4].getOutput()).getLongValue(); } - if (_args.length == 6) { - if (!_args[5].getOutput().isScalarBoolean()) + if(_args.length == 6) { + if(!_args[5].getOutput().isScalarBoolean()) raiseValidateError( - "The 6th ctable parameter (outputEmptyBlocks) must be a boolean literal.", - conditional); + "The 6th ctable parameter (outputEmptyBlocks) must be a boolean literal.", + conditional); } break; default: - raiseValidateError("Invalid number of arguments to table(): " - + this.toString(), conditional, LanguageErrorCodes.INVALID_PARAMETERS); + raiseValidateError("Invalid number of arguments to table(): " + this.toString(), conditional, + LanguageErrorCodes.INVALID_PARAMETERS); } // The dimensions for the output matrix will be known only at the // run time @@ -1200,12 +1199,13 @@ else if (getAllExpr().length == 1) { case MOMENT: checkMatrixParam(getFirstExpr()); - if (getThirdExpr() != null) { + if(getThirdExpr() != null) { checkNumParameters(3); checkMatrixParam(getSecondExpr()); checkMatchingDimensions(getFirstExpr(), getSecondExpr()); checkScalarParam(getThirdExpr()); - } else { + } + else { checkNumParameters(2); checkScalarParam(getSecondExpr()); } @@ -1221,16 +1221,17 @@ else if (getAllExpr().length == 1) { /* * x = cov(V1,V2) or xw = cov(V1,V2,W) */ - if (getThirdExpr() != null) { + if(getThirdExpr() != null) { checkNumParameters(3); - } else { + } + else { checkNumParameters(2); } checkMatrixParam(getFirstExpr()); checkMatrixParam(getSecondExpr()); checkMatchingDimensions(getFirstExpr(), getSecondExpr()); - if (getThirdExpr() != null) { + if(getThirdExpr() != null) { checkMatrixParam(getThirdExpr()); checkMatchingDimensions(getFirstExpr(), getThirdExpr()); } @@ -1244,15 +1245,14 @@ else if (getAllExpr().length == 1) { case QUANTILE: /* - * q = quantile(V1,0.5) computes median in V1 - * or Q = quantile(V1,P) computes the vector of quantiles as specified by P - * or qw = quantile(V1,W,0.5) computes median when weights (W) are given - * or QW = quantile(V1,W,P) computes the vector of quantiles as specified by P, - * when weights (W) are given + * q = quantile(V1,0.5) computes median in V1 or Q = quantile(V1,P) computes the vector of quantiles as + * specified by P or qw = quantile(V1,W,0.5) computes median when weights (W) are given or QW = + * quantile(V1,W,P) computes the vector of quantiles as specified by P, when weights (W) are given */ - if (getThirdExpr() != null) { + if(getThirdExpr() != null) { checkNumParameters(3); - } else { + } + else { checkNumParameters(2); } @@ -1260,7 +1260,7 @@ else if (getAllExpr().length == 1) { check1DMatrixParam(getFirstExpr()); // check for matching dimensions for other matrix parameters - if (getThirdExpr() != null) { + if(getThirdExpr() != null) { checkMatrixParam(getSecondExpr()); checkMatchingDimensions(getFirstExpr(), getSecondExpr()); } @@ -1269,11 +1269,12 @@ else if (getAllExpr().length == 1) { // output dimensions = dimensions of second, if third is null // = dimensions of the third, otherwise. - if (getThirdExpr() != null) { + if(getThirdExpr() != null) { output.setDimensions(getThirdExpr().getOutput().getDim1(), getThirdExpr().getOutput().getDim2()); output.setBlocksize(getThirdExpr().getOutput().getBlocksize()); output.setDataType(getThirdExpr().getOutput().getDataType()); - } else { + } + else { output.setDimensions(getSecondExpr().getOutput().getDim1(), getSecondExpr().getOutput().getDim2()); output.setBlocksize(getSecondExpr().getOutput().getBlocksize()); output.setDataType(getSecondExpr().getOutput().getDataType()); @@ -1281,23 +1282,24 @@ else if (getAllExpr().length == 1) { break; case INTERQUANTILE: - if (getThirdExpr() != null) { + if(getThirdExpr() != null) { checkNumParameters(3); - } else { + } + else { checkNumParameters(2); } checkMatrixParam(getFirstExpr()); - if (getThirdExpr() != null) { + if(getThirdExpr() != null) { // i.e., second input is weight vector checkMatrixParam(getSecondExpr()); checkMatchingDimensionsQuantile(); } - if ((getThirdExpr() == null && getSecondExpr().getOutput().getDataType() != DataType.SCALAR) - && (getThirdExpr() != null && getThirdExpr().getOutput().getDataType() != DataType.SCALAR)) { + if((getThirdExpr() == null && getSecondExpr().getOutput().getDataType() != DataType.SCALAR) && + (getThirdExpr() != null && getThirdExpr().getOutput().getDataType() != DataType.SCALAR)) { raiseValidateError("Invalid parameters to " + this.getOpCode(), conditional, - LanguageErrorCodes.INVALID_PARAMETERS); + LanguageErrorCodes.INVALID_PARAMETERS); } output.setValueType(id.getValueType()); @@ -1311,14 +1313,15 @@ else if (getAllExpr().length == 1) { /* * Usage: iqm = InterQuartileMean(A,W); iqm = InterQuartileMean(A); */ - if (getSecondExpr() != null) { + if(getSecondExpr() != null) { checkNumParameters(2); - } else { + } + else { checkNumParameters(1); } checkMatrixParam(getFirstExpr()); - if (getSecondExpr() != null) { + if(getSecondExpr() != null) { // i.e., second input is weight vector checkMatrixParam(getSecondExpr()); checkMatchingDimensions(getFirstExpr(), getSecondExpr()); @@ -1348,7 +1351,7 @@ else if (getAllExpr().length == 1) { checkNumParameters((getSecondExpr() != null) ? 2 : 1); checkMatrixParam(getFirstExpr()); - if (getSecondExpr() != null) { + if(getSecondExpr() != null) { // i.e., second input is weight vector checkMatrixParam(getSecondExpr()); checkMatchingDimensions(getFirstExpr(), getSecondExpr()); @@ -1365,52 +1368,51 @@ else if (getAllExpr().length == 1) { case SAMPLE: { Expression[] in = getAllExpr(); - for (Expression e : in) + for(Expression e : in) checkScalarParam(e); - if (in[0].getOutput().getValueType() != ValueType.FP64 - && in[0].getOutput().getValueType() != ValueType.INT64) + if(in[0].getOutput().getValueType() != ValueType.FP64 && + in[0].getOutput().getValueType() != ValueType.INT64) throw new LanguageException("First argument to sample() must be a number."); - if (in[1].getOutput().getValueType() != ValueType.FP64 - && in[1].getOutput().getValueType() != ValueType.INT64) + if(in[1].getOutput().getValueType() != ValueType.FP64 && + in[1].getOutput().getValueType() != ValueType.INT64) throw new LanguageException("Second argument to sample() must be a number."); boolean check = false; - if (isConstant(in[0]) && isConstant(in[1])) { + if(isConstant(in[0]) && isConstant(in[1])) { long range = ((ConstIdentifier) in[0]).getLongValue(); long size = ((ConstIdentifier) in[1]).getLongValue(); - if (range < size) + if(range < size) check = true; } - if (in.length == 4) { + if(in.length == 4) { checkNumParameters(4); - if (in[3].getOutput().getValueType() != ValueType.INT64) + if(in[3].getOutput().getValueType() != ValueType.INT64) throw new LanguageException("Fourth argument, seed, to sample() must be an integer value."); - if (in[2].getOutput().getValueType() != ValueType.BOOLEAN) + if(in[2].getOutput().getValueType() != ValueType.BOOLEAN) throw new LanguageException( - "Third argument to sample() must either denote replacement policy (boolean) or seed (integer)."); - } else if (in.length == 3) { + "Third argument to sample() must either denote replacement policy (boolean) or seed (integer)."); + } + else if(in.length == 3) { checkNumParameters(3); - if (in[2].getOutput().getValueType() != ValueType.BOOLEAN - && in[2].getOutput().getValueType() != ValueType.INT64) + if(in[2].getOutput().getValueType() != ValueType.BOOLEAN && + in[2].getOutput().getValueType() != ValueType.INT64) throw new LanguageException( - "Third argument to sample() must either denote replacement policy (boolean) or seed (integer)."); + "Third argument to sample() must either denote replacement policy (boolean) or seed (integer)."); } - if (check && in.length >= 3 - && isConstant(in[2]) - && in[2].getOutput().getValueType() == ValueType.BOOLEAN - && !((BooleanIdentifier) in[2]).getValue()) - throw new LanguageException("Sample (size=" + ((ConstIdentifier) in[0]).getLongValue() - + ") larger than population (size=" + ((ConstIdentifier) in[1]).getLongValue() - + ") can only be generated with replacement."); + if(check && in.length >= 3 && isConstant(in[2]) && + in[2].getOutput().getValueType() == ValueType.BOOLEAN && !((BooleanIdentifier) in[2]).getValue()) + throw new LanguageException( + "Sample (size=" + ((ConstIdentifier) in[0]).getLongValue() + ") larger than population (size=" + + ((ConstIdentifier) in[1]).getLongValue() + ") can only be generated with replacement."); // Output is a column vector output.setDataType(DataType.MATRIX); output.setValueType(ValueType.FP64); - if (isConstant(in[1])) + if(isConstant(in[1])) output.setDimensions(((ConstIdentifier) in[1]).getLongValue(), 1); else output.setDimensions(-1, 1); @@ -1423,29 +1425,30 @@ && isConstant(in[2]) // basic parameter validation checkScalarParam(getFirstExpr()); checkScalarParam(getSecondExpr()); - if (getThirdExpr() != null) { + if(getThirdExpr() != null) { checkNumParameters(3); checkScalarParam(getThirdExpr()); - } else + } + else checkNumParameters(2); // constant propagation (from, to, incr) - if (!conditional) { - if (getFirstExpr() instanceof DataIdentifier - && constVars.containsKey(((DataIdentifier) getFirstExpr()).getName())) + if(!conditional) { + if(getFirstExpr() instanceof DataIdentifier && + constVars.containsKey(((DataIdentifier) getFirstExpr()).getName())) _args[0] = constVars.get(((DataIdentifier) getFirstExpr()).getName()); - if (getSecondExpr() instanceof DataIdentifier - && constVars.containsKey(((DataIdentifier) getSecondExpr()).getName())) + if(getSecondExpr() instanceof DataIdentifier && + constVars.containsKey(((DataIdentifier) getSecondExpr()).getName())) _args[1] = constVars.get(((DataIdentifier) getSecondExpr()).getName()); - if (getThirdExpr() != null && getThirdExpr() instanceof DataIdentifier - && constVars.containsKey(((DataIdentifier) getThirdExpr()).getName())) + if(getThirdExpr() != null && getThirdExpr() instanceof DataIdentifier && + constVars.containsKey(((DataIdentifier) getThirdExpr()).getName())) _args[2] = constVars.get(((DataIdentifier) getThirdExpr()).getName()); } // check if dimensions can be inferred long dim1 = -1, dim2 = 1; - if (isConstant(getFirstExpr()) && isConstant(getSecondExpr()) - && (getThirdExpr() != null ? isConstant(getThirdExpr()) : true)) { + if(isConstant(getFirstExpr()) && isConstant(getSecondExpr()) && + (getThirdExpr() != null ? isConstant(getThirdExpr()) : true)) { double from, to, incr; try { from = getDoubleValue(getFirstExpr()); @@ -1453,17 +1456,18 @@ && isConstant(in[2]) // Setup the value of increment // default value: 1 if from <= to; -1 if from > to - if (getThirdExpr() == null) { + if(getThirdExpr() == null) { expandArguments(); _args[2] = new DoubleIdentifier(((from > to) ? -1.0 : 1.0), this); } incr = getDoubleValue(getThirdExpr()); - } catch (LanguageException e) { + } + catch(LanguageException e) { throw new LanguageException("Arguments for seq() must be numeric."); } - if ((from > to) && (incr >= 0)) + if((from > to) && (incr >= 0)) throw new LanguageException("Wrong sign for the increment in a call to seq()"); // Both end points of the range must included i.e., [from,to] both inclusive. @@ -1483,12 +1487,12 @@ && isConstant(in[2]) checkMatrixParam(getFirstExpr()); checkMatrixParam(getSecondExpr()); - if (getSecondExpr().getOutput().dimsKnown() && !is1DMatrix(getSecondExpr())) + if(getSecondExpr().getOutput().dimsKnown() && !is1DMatrix(getSecondExpr())) raiseValidateError("Second input to solve() must be a vector", conditional); - if (getFirstExpr().getOutput().dimsKnown() && getSecondExpr().getOutput().dimsKnown() && - getFirstExpr().getOutput().getDim1() != getSecondExpr().getOutput().getDim1() && - getFirstExpr().getOutput().getDim1() != getFirstExpr().getOutput().getDim2()) + if(getFirstExpr().getOutput().dimsKnown() && getSecondExpr().getOutput().dimsKnown() && + getFirstExpr().getOutput().getDim1() != getSecondExpr().getOutput().getDim1() && + getFirstExpr().getOutput().getDim1() != getFirstExpr().getOutput().getDim2()) raiseValidateError("Dimension mismatch in a call to solve()", conditional); output.setDataType(DataType.MATRIX); @@ -1505,9 +1509,9 @@ && isConstant(in[2]) output.setValueType(ValueType.FP64); Identifier in = getFirstExpr().getOutput(); - if (in.dimsKnown() && in.getDim1() != in.getDim2()) + if(in.dimsKnown() && in.getDim1() != in.getDim2()) raiseValidateError("Input to inv() must be square matrix -- given: a " + in.getDim1() + "x" - + in.getDim2() + " matrix.", conditional); + + in.getDim2() + " matrix.", conditional); output.setDimensions(in.getDim1(), in.getDim2()); output.setBlocksize(in.getBlocksize()); @@ -1522,9 +1526,9 @@ && isConstant(in[2]) output.setValueType(ValueType.FP64); Identifier inA = getFirstExpr().getOutput(); - if (inA.dimsKnown() && inA.getDim1() != inA.getDim2()) + if(inA.dimsKnown() && inA.getDim1() != inA.getDim2()) raiseValidateError("Input to cholesky() must be square matrix -- given: a " + inA.getDim1() + "x" - + inA.getDim2() + " matrix.", conditional); + + inA.getDim2() + " matrix.", conditional); output.setDimensions(inA.getDim1(), inA.getDim2()); output.setBlocksize(inA.getBlocksize()); @@ -1540,10 +1544,9 @@ && isConstant(in[2]) checkMatrixParam(getSecondExpr()); checkScalarParam(getThirdExpr()); checkValueTypeParam(getThirdExpr(), ValueType.STRING); - if (id.getDim2() > 1 || id2.getDim1() > 1) { - raiseValidateError("Outer vector operations require a common dimension of one: " + - id.getDim1() + "x" + id.getDim2() + " o " + id2.getDim1() + "x" + id2.getDim2() + ".", - false); + if(id.getDim2() > 1 || id2.getDim1() > 1) { + raiseValidateError("Outer vector operations require a common dimension of one: " + id.getDim1() + + "x" + id.getDim2() + " o " + id2.getDim1() + "x" + id2.getDim2() + ".", false); } // set output characteristics @@ -1587,7 +1590,7 @@ && isConstant(in[2]) // this is filter Expression input2 = null; - if (!(this.getOpCode() == Builtins.MAX_POOL || this.getOpCode() == Builtins.AVG_POOL)) { + if(!(this.getOpCode() == Builtins.MAX_POOL || this.getOpCode() == Builtins.AVG_POOL)) { input2 = _args[1]; // For conv2d_backward functions, this is dout checkMatrixParam(input2); } @@ -1595,14 +1598,15 @@ && isConstant(in[2]) output.setValueType(ValueType.FP64); output.setBlocksize(input.getOutput().getBlocksize()); - if (this.getOpCode() == Builtins.MAX_POOL_BACKWARD || this.getOpCode() == Builtins.AVG_POOL_BACKWARD) { + if(this.getOpCode() == Builtins.MAX_POOL_BACKWARD || this.getOpCode() == Builtins.AVG_POOL_BACKWARD) { output.setDimensions(input.getOutput().getDim1(), input.getOutput().getDim2()); - } else { + } + else { // stride1, stride2, padding1, padding2, numImg, numChannels, imgSize, imgSize, // filter_shape1=1, filter_shape2=1, filterSize/poolSize1, filterSize/poolSize1 try { int start = 2; - if (!(this.getOpCode() == Builtins.MAX_POOL || this.getOpCode() == Builtins.AVG_POOL)) { + if(!(this.getOpCode() == Builtins.MAX_POOL || this.getOpCode() == Builtins.AVG_POOL)) { start = 1; } long stride_h = (long) getDoubleValue(_args[start++]); @@ -1614,7 +1618,7 @@ && isConstant(in[2]) long H = (long) getDoubleValue(_args[start++]); long W = (long) getDoubleValue(_args[start++]); long K = -1; - if (!(this.getOpCode() == Builtins.MAX_POOL || this.getOpCode() == Builtins.AVG_POOL)) { + if(!(this.getOpCode() == Builtins.MAX_POOL || this.getOpCode() == Builtins.AVG_POOL)) { K = (long) getDoubleValue(_args[start]); } start++; @@ -1622,38 +1626,42 @@ && isConstant(in[2]) long R = (long) getDoubleValue(_args[start++]); long S = (long) getDoubleValue(_args[start++]); - if (this.getOpCode() == Builtins.CONV2D_BACKWARD_FILTER) { + if(this.getOpCode() == Builtins.CONV2D_BACKWARD_FILTER) { output.setDimensions(K, C * R * S); - } else if (this.getOpCode() == Builtins.CONV2D_BACKWARD_DATA) { + } + else if(this.getOpCode() == Builtins.CONV2D_BACKWARD_DATA) { output.setDimensions(N, C * H * W); - } else if (H > 0 && W > 0 && stride_h > 0 && stride_w > 0 && pad_h >= 0 && pad_w >= 0 && R > 0 - && S > 0) { + } + else if(H > 0 && W > 0 && stride_h > 0 && stride_w > 0 && pad_h >= 0 && pad_w >= 0 && R > 0 && + S > 0) { long P = DnnUtils.getP(H, R, stride_h, pad_h); long Q = DnnUtils.getQ(W, S, stride_w, pad_w); // Try to set both rows and columns - if (this.getOpCode() == Builtins.CONV2D) + if(this.getOpCode() == Builtins.CONV2D) output.setDimensions(N, K * P * Q); - else if (this.getOpCode() == Builtins.MAX_POOL || this.getOpCode() == Builtins.AVG_POOL) + else if(this.getOpCode() == Builtins.MAX_POOL || this.getOpCode() == Builtins.AVG_POOL) output.setDimensions(N, C * P * Q); else throw new LanguageException(""); - } else { + } + else { // Since columns cannot be computed, set only rows - if (this.getOpCode() == Builtins.CONV2D) + if(this.getOpCode() == Builtins.CONV2D) output.setDimensions(input.getOutput().getDim1(), -1); - else if (this.getOpCode() == Builtins.MAX_POOL || this.getOpCode() == Builtins.AVG_POOL) + else if(this.getOpCode() == Builtins.MAX_POOL || this.getOpCode() == Builtins.AVG_POOL) output.setDimensions(input.getOutput().getDim1(), -1); else throw new LanguageException(""); } - } catch (Exception e) { + } + catch(Exception e) { output.setDimensions(-1, -1); // To make sure that output dimensions are not incorrect even if // getDoubleValue doesnot return value } } checkMatrixParam(input); - if (input2 != null) + if(input2 != null) checkMatrixParam(input2); break; } @@ -1699,37 +1707,40 @@ else if (this.getOpCode() == Builtins.MAX_POOL || this.getOpCode() == Builtins.A checkNumParameters(getThirdExpr() != null ? 3 : 2); checkMatrixFrameParam(getFirstExpr()); checkScalarParam(getSecondExpr()); - if (getThirdExpr() != null) + if(getThirdExpr() != null) checkScalarParam(getThirdExpr()); // margin output.setDataType(DataType.FRAME); - if (_args[1].getText().contains("jaccardSim")) { + if(_args[1].getText().contains("jaccardSim")) { output.setDimensions(id.getDim1(), id.getDim1()); output.setValueType(ValueType.FP64); - } else { + } + else { output.setDimensions(id.getDim1(), id.getDim2()); output.setValueType(ValueType.STRING); } break; case LOCAL: - if (OptimizerUtils.ALLOW_SCRIPT_LEVEL_LOCAL_COMMAND) { + if(OptimizerUtils.ALLOW_SCRIPT_LEVEL_LOCAL_COMMAND) { checkNumParameters(1); checkMatrixParam(getFirstExpr()); output.setDataType(DataType.MATRIX); output.setDimensions(id.getDim1(), id.getDim2()); output.setBlocksize(id.getBlocksize()); output.setValueType(id.getValueType()); - } else + } + else raiseValidateError("Local instruction not allowed in dml script"); case COMPRESS: case DECOMPRESS: - if (OptimizerUtils.ALLOW_SCRIPT_LEVEL_COMPRESS_COMMAND) { + if(OptimizerUtils.ALLOW_SCRIPT_LEVEL_COMPRESS_COMMAND) { checkNumParameters(1); checkMatrixParam(getFirstExpr()); output.setDataType(DataType.MATRIX); output.setDimensions(id.getDim1(), id.getDim2()); output.setBlocksize(id.getBlocksize()); output.setValueType(id.getValueType()); - } else + } + else raiseValidateError("Compress/DeCompress instruction not allowed in dml script"); break; case ROW_COUNT_DISTINCT: @@ -1753,13 +1764,14 @@ else if (this.getOpCode() == Builtins.MAX_POOL || this.getOpCode() == Builtins.A break; default: - if (isMathFunction()) { + if(isMathFunction()) { checkMathFunctionParam(); // unary operations - if (getSecondExpr() == null) { + if(getSecondExpr() == null) { output.setDataType(id.getDataType()); - output.setValueType((output.getDataType() == DataType.SCALAR - && getOpCode() == Builtins.ABS) ? id.getValueType() : ValueType.FP64); + output + .setValueType((output.getDataType() == DataType.SCALAR && getOpCode() == Builtins.ABS) ? id + .getValueType() : ValueType.FP64); output.setDimensions(id.getDim1(), id.getDim2()); output.setBlocksize(id.getBlocksize()); } @@ -1767,17 +1779,18 @@ else if (this.getOpCode() == Builtins.MAX_POOL || this.getOpCode() == Builtins.A else { setBinaryOutputProperties(output); // override computed value type for special cases - if (getOpCode() == Builtins.LOG) + if(getOpCode() == Builtins.LOG) output.setValueType(ValueType.FP64); } - } else { + } + else { // always unconditional (because unsupported operation) Builtins op = getOpCode(); - if (op == Builtins.EIGEN || op == Builtins.LU || op == Builtins.QR || op == Builtins.SVD - || op == Builtins.LSTM || op == Builtins.LSTM_BACKWARD - || op == Builtins.BATCH_NORM2D || op == Builtins.BATCH_NORM2D_BACKWARD) + if(op == Builtins.EIGEN || op == Builtins.LU || op == Builtins.QR || op == Builtins.SVD || + op == Builtins.LSTM || op == Builtins.LSTM_BACKWARD || op == Builtins.BATCH_NORM2D || + op == Builtins.BATCH_NORM2D_BACKWARD) raiseValidateError("Function " + op + " needs to be called with multi-return assignment.", - false, LanguageErrorCodes.INVALID_PARAMETERS); + false, LanguageErrorCodes.INVALID_PARAMETERS); else raiseValidateError("Unsupported function " + op, false, LanguageErrorCodes.INVALID_PARAMETERS); } @@ -1788,12 +1801,12 @@ private void setBinaryOutputProperties(DataIdentifier output) { DataType dt1 = getFirstExpr().getOutput().getDataType(); DataType dt2 = getSecondExpr().getOutput().getDataType(); DataType dtOut = (dt1 == DataType.MATRIX || dt2 == DataType.MATRIX) ? DataType.MATRIX : DataType.SCALAR; - if (dt1 == DataType.MATRIX && dt2 == DataType.MATRIX) + if(dt1 == DataType.MATRIX && dt2 == DataType.MATRIX) checkMatchingDimensions(getFirstExpr(), getSecondExpr(), true); MatrixCharacteristics dims = getBinaryMatrixCharacteristics(getFirstExpr(), getSecondExpr()); output.setDataType(dtOut); output.setValueType( - dtOut == DataType.MATRIX ? ValueType.FP64 : computeValueType(getFirstExpr(), getSecondExpr(), true)); + dtOut == DataType.MATRIX ? ValueType.FP64 : computeValueType(getFirstExpr(), getSecondExpr(), true)); output.setDimensions(dims.getRows(), dims.getCols()); output.setBlocksize(dims.getBlocksize()); } @@ -1803,42 +1816,42 @@ private void setTernaryOutputProperties(DataIdentifier output, boolean condition DataType dt2 = getSecondExpr().getOutput().getDataType(); DataType dt3 = getThirdExpr().getOutput().getDataType(); DataType dtOut = (dt1.isMatrix() || dt2.isMatrix() || dt3.isMatrix()) ? DataType.MATRIX : DataType.SCALAR; - if (dt1 == DataType.MATRIX && dt2 == DataType.MATRIX) + if(dt1 == DataType.MATRIX && dt2 == DataType.MATRIX) checkMatchingDimensions(getFirstExpr(), getSecondExpr(), false, conditional); - if (dt1 == DataType.MATRIX && dt3 == DataType.MATRIX) + if(dt1 == DataType.MATRIX && dt3 == DataType.MATRIX) checkMatchingDimensions(getFirstExpr(), getThirdExpr(), false, conditional); - if (dt2 == DataType.MATRIX && dt3 == DataType.MATRIX) + if(dt2 == DataType.MATRIX && dt3 == DataType.MATRIX) checkMatchingDimensions(getSecondExpr(), getThirdExpr(), false, conditional); MatrixCharacteristics dims1 = getBinaryMatrixCharacteristics(getFirstExpr(), getSecondExpr()); MatrixCharacteristics dims2 = getBinaryMatrixCharacteristics(getSecondExpr(), getThirdExpr()); output.setDataType(dtOut); output.setValueType( - dtOut == DataType.MATRIX ? ValueType.FP64 : computeValueType(getSecondExpr(), getThirdExpr(), true)); + dtOut == DataType.MATRIX ? ValueType.FP64 : computeValueType(getSecondExpr(), getThirdExpr(), true)); output.setDimensions(Math.max(dims1.getRows(), dims2.getRows()), Math.max(dims1.getCols(), dims2.getCols())); output.setBlocksize(Math.max(dims1.getBlocksize(), dims2.getBlocksize())); } private void setNaryOutputProperties(DataIdentifier output) { - DataType dt = Arrays.stream(getAllExpr()).allMatch( - e -> e.getOutput().getDataType().isScalar()) ? DataType.SCALAR : DataType.MATRIX; - Expression firstM = dt.isMatrix() ? Arrays.stream(getAllExpr()).filter( - e -> e.getOutput().getDataType().isMatrix()).findFirst().get() : null; + DataType dt = Arrays.stream(getAllExpr()) + .allMatch(e -> e.getOutput().getDataType().isScalar()) ? DataType.SCALAR : DataType.MATRIX; + Expression firstM = dt.isMatrix() ? Arrays.stream(getAllExpr()) + .filter(e -> e.getOutput().getDataType().isMatrix()).findFirst().get() : null; ValueType vt = dt.isMatrix() ? ValueType.FP64 : ValueType.INT64; - for (Expression e : getAllExpr()) { + for(Expression e : getAllExpr()) { vt = computeValueType(e, e.getOutput().getValueType(), vt, true); - if (e.getOutput().getDataType().isMatrix()) + if(e.getOutput().getDataType().isMatrix()) checkMatchingDimensions(firstM, e, true); } output.setDataType(dt); output.setValueType(vt); output.setDimensions(dt.isMatrix() ? firstM.getOutput().getDim1() : 0, - dt.isMatrix() ? firstM.getOutput().getDim2() : 0); + dt.isMatrix() ? firstM.getOutput().getDim2() : 0); output.setBlocksize(dt.isMatrix() ? firstM.getOutput().getBlocksize() : 0); } private void expandArguments() { - if (_args == null) { + if(_args == null) { _args = new Expression[1]; return; } @@ -1853,20 +1866,20 @@ public boolean multipleReturns() { } private static boolean isConstant(Expression expr) { - return (expr != null && expr instanceof ConstIdentifier); + return(expr != null && expr instanceof ConstIdentifier); } private static double getDoubleValue(Expression expr) { - if (expr instanceof DoubleIdentifier) + if(expr instanceof DoubleIdentifier) return ((DoubleIdentifier) expr).getValue(); - else if (expr instanceof IntIdentifier) + else if(expr instanceof IntIdentifier) return ((IntIdentifier) expr).getValue(); else throw new LanguageException("Expecting a numeric value."); } private boolean isMathFunction() { - switch (this.getOpCode()) { + switch(this.getOpCode()) { case COS: case SIN: case TAN: @@ -1898,7 +1911,7 @@ private boolean isMathFunction() { } private void checkMathFunctionParam() { - switch (this.getOpCode()) { + switch(this.getOpCode()) { case COS: case SIN: case TAN: @@ -1919,9 +1932,10 @@ private void checkMathFunctionParam() { checkNumParameters(1); break; case LOG: - if (getSecondExpr() != null) { + if(getSecondExpr() != null) { checkNumParameters(2); - } else { + } + else { checkNumParameters(1); } break; @@ -1934,9 +1948,9 @@ private void checkMathFunctionParam() { @Override public String toString() { StringBuilder sb = new StringBuilder(_opcode.toString() + "("); - if (_args != null) { - for (int i = 0; i < _args.length; i++) { - if (i > 0) { + if(_args != null) { + for(int i = 0; i < _args.length; i++) { + if(i > 0) { sb.append(","); } sb.append(_args[i].toString()); @@ -1951,7 +1965,7 @@ public String toString() { public VariableSet variablesRead() { VariableSet result = new VariableSet(); - for (int i = 0; i < _args.length; i++) { + for(int i = 0; i < _args.length; i++) { result.addVariables(_args[i].variablesRead()); } @@ -1966,113 +1980,116 @@ public VariableSet variablesUpdated() { } protected void checkNumParameters(int count) { // always unconditional - if (getFirstExpr() == null && _args.length > 0) { + if(getFirstExpr() == null && _args.length > 0) { raiseValidateError("Missing argument for function " + this.getOpCode(), false, - LanguageErrorCodes.INVALID_PARAMETERS); + LanguageErrorCodes.INVALID_PARAMETERS); } // Not sure the rationale for the first two if loops, but will keep them for // backward compatibility - if (((count == 1) && (getSecondExpr() != null || getThirdExpr() != null)) - || ((count == 2) && (getThirdExpr() != null))) { + if(((count == 1) && (getSecondExpr() != null || getThirdExpr() != null)) || + ((count == 2) && (getThirdExpr() != null))) { raiseValidateError("Invalid number of arguments for function " + this.getOpCode().toString().toLowerCase() - + "(). This function only takes 1 or 2 arguments.", false); - } else if (((count == 2) && (getSecondExpr() == null)) - || ((count == 3) && (getSecondExpr() == null || getThirdExpr() == null))) { + + "(). This function only takes 1 or 2 arguments.", false); + } + else if(((count == 2) && (getSecondExpr() == null)) || + ((count == 3) && (getSecondExpr() == null || getThirdExpr() == null))) { raiseValidateError("Missing argument for function " + this.getOpCode(), false, - LanguageErrorCodes.INVALID_PARAMETERS); - } else if (count > 0 && (_args == null || _args.length < count)) { + LanguageErrorCodes.INVALID_PARAMETERS); + } + else if(count > 0 && (_args == null || _args.length < count)) { raiseValidateError("Missing argument for function " + this.getOpCode(), false, - LanguageErrorCodes.INVALID_PARAMETERS); - } else if (count == 0 && (_args.length > 0 - || getSecondExpr() != null || getThirdExpr() != null)) { - raiseValidateError("Missing argument for function " + this.getOpCode() - + "(). This function doesn't take any arguments.", false); + LanguageErrorCodes.INVALID_PARAMETERS); + } + else if(count == 0 && (_args.length > 0 || getSecondExpr() != null || getThirdExpr() != null)) { + raiseValidateError( + "Missing argument for function " + this.getOpCode() + "(). This function doesn't take any arguments.", + false); } } protected void checkMatrixParam(Expression e) { - if (e.getOutput().getDataType() != DataType.MATRIX) { - raiseValidateError("Expected " + e.getText() + " to be a matrix argument for function " + - this.getOpCode().toString().toLowerCase() + "().", false); + if(e.getOutput().getDataType() != DataType.MATRIX) { + raiseValidateError("Expected " + e.getText() + " to be a matrix argument for function " + + this.getOpCode().toString().toLowerCase() + "().", false); } } protected void checkMatrixTensorParam(Expression e) { - if (e.getOutput().getDataType() != DataType.MATRIX) { + if(e.getOutput().getDataType() != DataType.MATRIX) { // Param is not a matrix // TODO get supported Operations form builtins - if (e.getOutput().getDataType() != DataType.TENSOR || getOpCode() != Builtins.SUM) { + if(e.getOutput().getDataType() != DataType.TENSOR || getOpCode() != Builtins.SUM) { // Param is also not a tensor, or the operation is not supported on tensor raiseValidateError("Expected " + e.getText() + " to be a matrix or tensor argument for function " - + this.getOpCode().toString().toLowerCase() + "().", false); + + this.getOpCode().toString().toLowerCase() + "().", false); } } } protected void checkDataTypeParam(Expression e, DataType... dt) { // always unconditional - if (!ArrayUtils.contains(dt, e.getOutput().getDataType())) + if(!ArrayUtils.contains(dt, e.getOutput().getDataType())) raiseValidateError("Non-matching expected data type for function " + getOpCode(), false, - LanguageErrorCodes.UNSUPPORTED_PARAMETERS); + LanguageErrorCodes.UNSUPPORTED_PARAMETERS); } protected void checkMatrixFrameParam(Expression e) { // always unconditional - if (e.getOutput().getDataType() != DataType.MATRIX && e.getOutput().getDataType() != DataType.FRAME) { + if(e.getOutput().getDataType() != DataType.MATRIX && e.getOutput().getDataType() != DataType.FRAME) { raiseValidateError("Expecting matrix or frame parameter for function " + getOpCode(), false, - LanguageErrorCodes.UNSUPPORTED_PARAMETERS); + LanguageErrorCodes.UNSUPPORTED_PARAMETERS); } } protected void checkMatrixScalarParam(Expression e) { // always unconditional - if (e.getOutput().getDataType() != DataType.MATRIX && e.getOutput().getDataType() != DataType.SCALAR) { + if(e.getOutput().getDataType() != DataType.MATRIX && e.getOutput().getDataType() != DataType.SCALAR) { raiseValidateError("Expecting matrix or scalar parameter for function " + getOpCode(), false, - LanguageErrorCodes.UNSUPPORTED_PARAMETERS); + LanguageErrorCodes.UNSUPPORTED_PARAMETERS); } } private void checkScalarParam(Expression e) { // always unconditional - if (e.getOutput().getDataType() != DataType.SCALAR) { + if(e.getOutput().getDataType() != DataType.SCALAR) { raiseValidateError("Expecting scalar parameter for function " + getOpCode(), false, - LanguageErrorCodes.UNSUPPORTED_PARAMETERS); + LanguageErrorCodes.UNSUPPORTED_PARAMETERS); } } private void checkListParam(Expression e) { // always unconditional - if (e.getOutput().getDataType() != DataType.LIST) { + if(e.getOutput().getDataType() != DataType.LIST) { raiseValidateError("Expecting scalar parameter for function " + getOpCode(), false, - LanguageErrorCodes.UNSUPPORTED_PARAMETERS); + LanguageErrorCodes.UNSUPPORTED_PARAMETERS); } } @SuppressWarnings("unused") private void checkScalarFrameParam(Expression e) { // always unconditional - if (e.getOutput().getDataType() != DataType.SCALAR && e.getOutput().getDataType() != DataType.FRAME) { + if(e.getOutput().getDataType() != DataType.SCALAR && e.getOutput().getDataType() != DataType.FRAME) { raiseValidateError("Expecting scalar parameter for function " + getOpCode(), false, - LanguageErrorCodes.UNSUPPORTED_PARAMETERS); + LanguageErrorCodes.UNSUPPORTED_PARAMETERS); } } private void checkValueTypeParam(Expression e, ValueType vt) { // always unconditional - if (e.getOutput().getValueType() != vt) { + if(e.getOutput().getValueType() != vt) { raiseValidateError("Expecting parameter of different value type " + this.getOpCode(), false, - LanguageErrorCodes.UNSUPPORTED_PARAMETERS); + LanguageErrorCodes.UNSUPPORTED_PARAMETERS); } } protected void checkStringOrDataIdentifier(Expression e) { // always unconditional - if (!(e.getOutput().getDataType().isScalar() && e.getOutput().getValueType() == ValueType.STRING) - && !(e instanceof DataIdentifier && !(e instanceof IndexedIdentifier))) { + if(!(e.getOutput().getDataType().isScalar() && e.getOutput().getValueType() == ValueType.STRING) && + !(e instanceof DataIdentifier && !(e instanceof IndexedIdentifier))) { raiseValidateError("Expecting variable name or data identifier " + getOpCode(), false, - LanguageErrorCodes.UNSUPPORTED_PARAMETERS); + LanguageErrorCodes.UNSUPPORTED_PARAMETERS); } } private static boolean is1DMatrix(Expression e) { - return (e.getOutput().getDim1() == 1 || e.getOutput().getDim2() == 1); + return(e.getOutput().getDim1() == 1 || e.getOutput().getDim2() == 1); } private static boolean dimsKnown(Expression e) { - return (e.getOutput().getDim1() != -1 && e.getOutput().getDim2() != -1); + return(e.getOutput().getDim1() != -1 && e.getOutput().getDim2() != -1); } private void check1DMatrixParam(Expression e) { // always unconditional @@ -2081,9 +2098,9 @@ private void check1DMatrixParam(Expression e) { // always unconditional // throw an exception, when e's output is NOT a one-dimensional matrix // the check must be performed only when the dimensions are known at compilation // time - if (dimsKnown(e) && !is1DMatrix(e)) { - raiseValidateError("Expecting one-dimensional matrix parameter for function " - + this.getOpCode(), false, LanguageErrorCodes.UNSUPPORTED_PARAMETERS); + if(dimsKnown(e) && !is1DMatrix(e)) { + raiseValidateError("Expecting one-dimensional matrix parameter for function " + this.getOpCode(), false, + LanguageErrorCodes.UNSUPPORTED_PARAMETERS); } } @@ -2096,59 +2113,55 @@ private void checkMatchingDimensions(Expression expr1, Expression expr2, boolean } private void checkMatchingDimensions(Expression expr1, Expression expr2, boolean allowsMV, boolean conditional) { - if (expr1 != null && expr2 != null) { + if(expr1 != null && expr2 != null) { // if any matrix has unknown dimensions, simply return - if (expr1.getOutput().getDim1() == -1 || expr2.getOutput().getDim1() == -1 - || expr1.getOutput().getDim2() == -1 || expr2.getOutput().getDim2() == -1) { + if(expr1.getOutput().getDim1() == -1 || expr2.getOutput().getDim1() == -1 || + expr1.getOutput().getDim2() == -1 || expr2.getOutput().getDim2() == -1) { return; - } else if ((!allowsMV && expr1.getOutput().getDim1() != expr2.getOutput().getDim1()) - || (allowsMV && expr1.getOutput().getDim1() != expr2.getOutput().getDim1() - && expr2.getOutput().getDim1() != 1) - || (!allowsMV && expr1.getOutput().getDim2() != expr2.getOutput().getDim2()) - || (allowsMV && expr1.getOutput().getDim2() != expr2.getOutput().getDim2() - && expr2.getOutput().getDim2() != 1)) { - raiseValidateError("Mismatch in matrix dimensions of parameters for function " - + this.getOpCode(), conditional, LanguageErrorCodes.INVALID_PARAMETERS); + } + else if((!allowsMV && expr1.getOutput().getDim1() != expr2.getOutput().getDim1()) || + (allowsMV && expr1.getOutput().getDim1() != expr2.getOutput().getDim1() && + expr2.getOutput().getDim1() != 1) || + (!allowsMV && expr1.getOutput().getDim2() != expr2.getOutput().getDim2()) || (allowsMV && + expr1.getOutput().getDim2() != expr2.getOutput().getDim2() && expr2.getOutput().getDim2() != 1)) { + raiseValidateError("Mismatch in matrix dimensions of parameters for function " + this.getOpCode(), + conditional, LanguageErrorCodes.INVALID_PARAMETERS); } } } private void checkMatchingDimensionsQuantile() { - if (getFirstExpr().getOutput().getDim1() != getSecondExpr().getOutput().getDim1()) { - raiseValidateError("Mismatch in matrix dimensions for " - + this.getOpCode(), false, LanguageErrorCodes.INVALID_PARAMETERS); + if(getFirstExpr().getOutput().getDim1() != getSecondExpr().getOutput().getDim1()) { + raiseValidateError("Mismatch in matrix dimensions for " + this.getOpCode(), false, + LanguageErrorCodes.INVALID_PARAMETERS); } } - public static BuiltinFunctionExpression getBuiltinFunctionExpression(ParserRuleContext ctx, - String functionName, ArrayList paramExprsPassed, String filename) { + public static BuiltinFunctionExpression getBuiltinFunctionExpression(ParserRuleContext ctx, String functionName, + ArrayList paramExprsPassed, String filename) { - if (functionName == null || paramExprsPassed == null) + if(functionName == null || paramExprsPassed == null) return null; // check if the function name is built-in function // (assign built-in function op if function is built-in - return (Builtins.contains(functionName, false, false) - && (paramExprsPassed.stream().anyMatch(p -> p.getName() == null) // at least one unnamed - || paramExprsPassed.size() == 0)) - ? new BuiltinFunctionExpression(ctx, Builtins.get(functionName), paramExprsPassed, - filename) - : null; + return (Builtins.contains(functionName, false, false) && + (paramExprsPassed.stream().anyMatch(p -> p.getName() == null) // at least one unnamed + || paramExprsPassed.size() == 0)) ? new BuiltinFunctionExpression(ctx, Builtins.get(functionName), + paramExprsPassed, filename) : null; } /** - * Convert a value type (double, int, or boolean) to a built-in function - * operator. + * Convert a value type (double, int, or boolean) to a built-in function operator. * - * @param vt Value type ({@code ValueType.DOUBLE}, {@code ValueType.INT}, or - * {@code ValueType.BOOLEAN}). - * @return Built-in function operator ({@code Builtins.AS_DOUBLE}, - * {@code Builtins.AS_INT}, or {@code Builtins.AS_BOOLEAN}). + * @param vt Value type ({@code ValueType.DOUBLE}, {@code ValueType.INT}, or {@code ValueType.BOOLEAN}). + * @return Built-in function operator ({@code Builtins.AS_DOUBLE}, {@code Builtins.AS_INT}, or + * {@code Builtins.AS_BOOLEAN}). */ public static Builtins getValueTypeCastOperator(ValueType vt) { - switch (vt) { + switch(vt) { case FP64: return Builtins.CAST_AS_DOUBLE; case INT64: diff --git a/src/main/java/org/apache/sysds/parser/DMLTranslator.java b/src/main/java/org/apache/sysds/parser/DMLTranslator.java index 537de1abbaa..ac0699f0b2f 100644 --- a/src/main/java/org/apache/sysds/parser/DMLTranslator.java +++ b/src/main/java/org/apache/sysds/parser/DMLTranslator.java @@ -92,35 +92,33 @@ import org.apache.sysds.runtime.instructions.Instruction; import org.apache.sysds.runtime.instructions.cp.VariableCPInstruction; -public class DMLTranslator -{ +public class DMLTranslator { private static final Log LOG = LogFactory.getLog(DMLTranslator.class.getName()); private DMLProgram _dmlProg; public DMLTranslator(DMLProgram dmlp) { _dmlProg = dmlp; - //setup default size for unknown dimensions + // setup default size for unknown dimensions OptimizerUtils.resetDefaultSize(); - //reinit rewriter according to opt level flags - Recompiler.reinitRecompiler(); + // reinit rewriter according to opt level flags + Recompiler.reinitRecompiler(); } public void validateParseTree(DMLProgram dmlp) { validateParseTree(dmlp, true); } - public void validateParseTree(DMLProgram dmlp, boolean inclFuns) - { - //STEP1: Pre-processing steps for validate - e.g., prepare read-after-write meta data + public void validateParseTree(DMLProgram dmlp, boolean inclFuns) { + // STEP1: Pre-processing steps for validate - e.g., prepare read-after-write meta data boolean fWriteRead = prepareReadAfterWrite(dmlp, new HashMap()); - //STEP2: Actual Validate - if( inclFuns ) { + // STEP2: Actual Validate + if(inclFuns) { // handle functions in namespaces (current program has default namespace) - for (String namespaceKey : dmlp.getNamespaces().keySet()) { + for(String namespaceKey : dmlp.getNamespaces().keySet()) { // for each function defined in the namespace - for (String fname : dmlp.getFunctionStatementBlocks(namespaceKey).keySet()) { - FunctionStatementBlock fblock = dmlp.getFunctionStatementBlock(namespaceKey,fname); + for(String fname : dmlp.getFunctionStatementBlocks(namespaceKey).keySet()) { + FunctionStatementBlock fblock = dmlp.getFunctionStatementBlock(namespaceKey, fname); validateFunction(dmlp, fblock); } } @@ -129,22 +127,21 @@ public void validateParseTree(DMLProgram dmlp, boolean inclFuns) // handle regular blocks -- "main" program VariableSet vs = new VariableSet(); HashMap constVars = new HashMap<>(); - for (int i = 0; i < dmlp.getNumStatementBlocks(); i++) { + for(int i = 0; i < dmlp.getNumStatementBlocks(); i++) { StatementBlock sb = dmlp.getStatementBlock(i); vs = sb.validate(dmlp, vs, constVars, fWriteRead); constVars = sb.getConstOut(); } - //STEP3: Post-processing steps after validate - e.g., prepare read-after-write meta data - if( fWriteRead ) - { - //propagate size and datatypes into read + // STEP3: Post-processing steps after validate - e.g., prepare read-after-write meta data + if(fWriteRead) { + // propagate size and datatypes into read prepareReadAfterWrite(dmlp, new HashMap<>()); - //re-validate main program for datatype propagation + // re-validate main program for datatype propagation vs = new VariableSet(); constVars = new HashMap<>(); - for (int i = 0; i < dmlp.getNumStatementBlocks(); i++) { + for(int i = 0; i < dmlp.getNumStatementBlocks(); i++) { StatementBlock sb = dmlp.getStatementBlock(i); vs = sb.validate(dmlp, vs, constVars, fWriteRead); constVars = sb.getConstOut(); @@ -161,9 +158,9 @@ public void validateFunction(DMLProgram dmlp, FunctionStatementBlock fsb, boolea VariableSet vs = new VariableSet(); // add the input variables for the function to input variable list - FunctionStatement fstmt = (FunctionStatement)fsb.getStatement(0); - for (DataIdentifier currVar : fstmt.getInputParams()) { - if (currVar.getDataType() == DataType.SCALAR) + FunctionStatement fstmt = (FunctionStatement) fsb.getStatement(0); + for(DataIdentifier currVar : fstmt.getInputParams()) { + if(currVar.getDataType() == DataType.SCALAR) currVar.setDimensions(0, 0); vs.addVariable(currVar.getName(), currVar); } @@ -177,31 +174,31 @@ public void liveVariableAnalysis(DMLProgram dmlp) { public void liveVariableAnalysis(DMLProgram dmlp, boolean inclFuns) { // for each namespace, handle function statement blocks - if( inclFuns ) { - for (String namespaceKey : dmlp.getNamespaces().keySet()) { - for (String fname: dmlp.getFunctionStatementBlocks(namespaceKey).keySet()) { + if(inclFuns) { + for(String namespaceKey : dmlp.getNamespaces().keySet()) { + for(String fname : dmlp.getFunctionStatementBlocks(namespaceKey).keySet()) { FunctionStatementBlock fsb = dmlp.getFunctionStatementBlock(namespaceKey, fname); liveVariableAnalysisFunction(dmlp, fsb); } } } - // handle regular program blocks + // handle regular program blocks VariableSet currentLiveOut = new VariableSet(); VariableSet activeIn = new VariableSet(); // handle function inlining dmlp.setStatementBlocks(StatementBlock.mergeFunctionCalls(dmlp.getStatementBlocks(), dmlp)); - for (int i = 0; i < dmlp.getNumStatementBlocks(); i++) { + for(int i = 0; i < dmlp.getNumStatementBlocks(); i++) { StatementBlock sb = dmlp.getStatementBlock(i); activeIn = sb.initializeforwardLV(activeIn); } - if (dmlp.getNumStatementBlocks() > 0){ + if(dmlp.getNumStatementBlocks() > 0) { StatementBlock lastSb = dmlp.getStatementBlock(dmlp.getNumStatementBlocks() - 1); lastSb._liveOut = new VariableSet(); - for (int i = dmlp.getNumStatementBlocks() - 1; i >= 0; i--) { + for(int i = dmlp.getNumStatementBlocks() - 1; i >= 0; i--) { StatementBlock sb = dmlp.getStatementBlock(i); currentLiveOut = sb.analyze(currentLiveOut); } @@ -211,27 +208,27 @@ public void liveVariableAnalysis(DMLProgram dmlp, boolean inclFuns) { } public void liveVariableAnalysisFunction(DMLProgram dmlp, FunctionStatementBlock fsb) { - //STEP 1: forward direction - FunctionStatement fstmt = (FunctionStatement)fsb.getStatement(0); + // STEP 1: forward direction + FunctionStatement fstmt = (FunctionStatement) fsb.getStatement(0); // perform function inlining fstmt.setBody(StatementBlock.mergeFunctionCalls(fstmt.getBody(), dmlp)); VariableSet activeIn = new VariableSet(); - for (DataIdentifier id : fstmt.getInputParams()){ - activeIn.addVariable(id.getName(), id); + for(DataIdentifier id : fstmt.getInputParams()) { + activeIn.addVariable(id.getName(), id); } fsb.initializeforwardLV(activeIn); - //STEP 2: backward direction + // STEP 2: backward direction VariableSet currentLiveOut = new VariableSet(); VariableSet currentLiveIn = new VariableSet(); VariableSet unionLiveIn = new VariableSet(); - for (DataIdentifier id : fstmt.getInputParams()) + for(DataIdentifier id : fstmt.getInputParams()) currentLiveIn.addVariable(id.getName(), id); - for (DataIdentifier id : fstmt.getOutputParams()) { + for(DataIdentifier id : fstmt.getOutputParams()) { currentLiveOut.addVariable(id.getName(), id); unionLiveIn.addVariable(id.getName(), id); } @@ -242,14 +239,13 @@ public void liveVariableAnalysisFunction(DMLProgram dmlp, FunctionStatementBlock } public void cleanupLiveOutVariables(List sbs, VariableSet unionLiveIn) { - //backwards pass to collect union of livein variables of all successors - //and cleanup unnecessary liveout variables - for(int i=sbs.size()-1; i>=0; i--) { + // backwards pass to collect union of livein variables of all successors + // and cleanup unnecessary liveout variables + for(int i = sbs.size() - 1; i >= 0; i--) { StatementBlock sb = sbs.get(i); - //remove liveout variables that are not in unionLivein - sb.liveOut().removeVariables( - VariableSet.minus(sb.liveOut(), unionLiveIn)); - //collect all livein information + // remove liveout variables that are not in unionLivein + sb.liveOut().removeVariables(VariableSet.minus(sb.liveOut(), unionLiveIn)); + // collect all livein information unionLiveIn.addVariables(sb.liveIn()); } } @@ -260,56 +256,54 @@ public void constructHops(DMLProgram dmlp) { public void constructHops(DMLProgram dmlp, boolean inclFuns) { // Step 1: construct hops for all functions - if( inclFuns ) { + if(inclFuns) { // for each namespace, handle function program blocks - for( FunctionDictionary fdict : dmlp.getNamespaces().values() ) - for( FunctionStatementBlock fsb : fdict.getFunctions().values() ) + for(FunctionDictionary fdict : dmlp.getNamespaces().values()) + for(FunctionStatementBlock fsb : fdict.getFunctions().values()) constructHops(fsb); } // Step 2: construct hops for main program // handle regular program blocks - for (int i = 0; i < dmlp.getNumStatementBlocks(); i++) { + for(int i = 0; i < dmlp.getNumStatementBlocks(); i++) { StatementBlock current = dmlp.getStatementBlock(i); constructHops(current); } } - public void rewriteHopsDAG(DMLProgram dmlp) - { - //apply hop rewrites (static rewrites) + public void rewriteHopsDAG(DMLProgram dmlp) { + // apply hop rewrites (static rewrites) ProgramRewriter rewriter = new ProgramRewriter(true, false); - rewriter.rewriteProgramHopDAGs(dmlp, false); //rewrite and merge + rewriter.rewriteProgramHopDAGs(dmlp, false); // rewrite and merge resetHopsDAGVisitStatus(dmlp); - rewriter.rewriteProgramHopDAGs(dmlp, true); //rewrite and split + rewriter.rewriteProgramHopDAGs(dmlp, true); // rewrite and split resetHopsDAGVisitStatus(dmlp); - //propagate size information from main into functions (but conservatively) - if( OptimizerUtils.ALLOW_INTER_PROCEDURAL_ANALYSIS ) { + // propagate size information from main into functions (but conservatively) + if(OptimizerUtils.ALLOW_INTER_PROCEDURAL_ANALYSIS) { InterProceduralAnalysis ipa = new InterProceduralAnalysis(dmlp); ipa.analyzeProgram(OptimizerUtils.IPA_NUM_REPETITIONS); resetHopsDAGVisitStatus(dmlp); } - //apply hop rewrites (dynamic rewrites, after IPA) + // apply hop rewrites (dynamic rewrites, after IPA) ProgramRewriter rewriter2 = new ProgramRewriter(false, true); rewriter2.rewriteProgramHopDAGs(dmlp); resetHopsDAGVisitStatus(dmlp); - //compute memory estimates for all the hops. These estimates are used - //subsequently in various optimizations, e.g. CP vs. MR scheduling and parfor. + // compute memory estimates for all the hops. These estimates are used + // subsequently in various optimizations, e.g. CP vs. MR scheduling and parfor. refreshMemEstimates(dmlp); resetHopsDAGVisitStatus(dmlp); - //enhance HOP DAGs by automatic operator fusion + // enhance HOP DAGs by automatic operator fusion DMLConfig dmlconf = ConfigurationManager.getDMLConfig(); - if( ConfigurationManager.isCodegenEnabled() ){ - SpoofCompiler.PLAN_CACHE_POLICY = PlanCachePolicy.get( - dmlconf.getBooleanValue(DMLConfig.CODEGEN_PLANCACHE), - dmlconf.getIntValue(DMLConfig.CODEGEN_LITERALS)==2); + if(ConfigurationManager.isCodegenEnabled()) { + SpoofCompiler.PLAN_CACHE_POLICY = PlanCachePolicy.get(dmlconf.getBooleanValue(DMLConfig.CODEGEN_PLANCACHE), + dmlconf.getIntValue(DMLConfig.CODEGEN_LITERALS) == 2); SpoofCompiler.setConfiguredPlanSelector(); SpoofCompiler.setExecTypeSpecificJavaCompiler(); - if( SpoofCompiler.INTEGRATION==IntegrationType.HOPS ) + if(SpoofCompiler.INTEGRATION == IntegrationType.HOPS) codgenHopsDAG(dmlp); } } @@ -333,33 +327,31 @@ public void codgenHopsDAG(ProgramBlock pb) { public void constructLops(DMLProgram dmlp) { // for each namespace, handle function program blocks - for( FunctionDictionary fdict : dmlp.getNamespaces().values() ) { - //handle optimized functions - for( FunctionStatementBlock fsb : fdict.getFunctions().values() ) + for(FunctionDictionary fdict : dmlp.getNamespaces().values()) { + // handle optimized functions + for(FunctionStatementBlock fsb : fdict.getFunctions().values()) constructLops(fsb); - //handle unoptimized functions - if( fdict.getFunctions(false) != null ) - for( FunctionStatementBlock fsb : fdict.getFunctions(false).values() ) + // handle unoptimized functions + if(fdict.getFunctions(false) != null) + for(FunctionStatementBlock fsb : fdict.getFunctions(false).values()) constructLops(fsb); } // handle regular program blocks - for( StatementBlock sb : dmlp.getStatementBlocks() ) + for(StatementBlock sb : dmlp.getStatementBlocks()) constructLops(sb); } - public boolean constructLops(StatementBlock sb) - { + public boolean constructLops(StatementBlock sb) { boolean ret = false; - if (sb instanceof WhileStatementBlock) - { - WhileStatementBlock wsb = (WhileStatementBlock)sb; - WhileStatement whileStmt = (WhileStatement)wsb.getStatement(0); + if(sb instanceof WhileStatementBlock) { + WhileStatementBlock wsb = (WhileStatementBlock) sb; + WhileStatement whileStmt = (WhileStatement) wsb.getStatement(0); ArrayList body = whileStmt.getBody(); // step through stmt blocks in while stmt body - for (StatementBlock stmtBlock : body) + for(StatementBlock stmtBlock : body) ret |= constructLops(stmtBlock); // handle while stmt predicate @@ -368,19 +360,18 @@ public boolean constructLops(StatementBlock sb) ret |= wsb.updatePredicateRecompilationFlag(); } - else if (sb instanceof IfStatementBlock) - { + else if(sb instanceof IfStatementBlock) { IfStatementBlock isb = (IfStatementBlock) sb; - IfStatement ifStmt = (IfStatement)isb.getStatement(0); + IfStatement ifStmt = (IfStatement) isb.getStatement(0); ArrayList ifBody = ifStmt.getIfBody(); ArrayList elseBody = ifStmt.getElseBody(); // step through stmt blocks in if stmt ifBody - for (StatementBlock stmtBlock : ifBody) + for(StatementBlock stmtBlock : ifBody) ret |= constructLops(stmtBlock); // step through stmt blocks in if stmt elseBody - for (StatementBlock stmtBlock : elseBody) + for(StatementBlock stmtBlock : elseBody) ret |= constructLops(stmtBlock); // handle if stmt predicate @@ -389,49 +380,49 @@ else if (sb instanceof IfStatementBlock) ret |= isb.updatePredicateRecompilationFlag(); } - else if (sb instanceof ForStatementBlock) //NOTE: applies to ForStatementBlock and ParForStatementBlock + else if(sb instanceof ForStatementBlock) // NOTE: applies to ForStatementBlock and ParForStatementBlock { - ForStatementBlock fsb = (ForStatementBlock) sb; - ForStatement fs = (ForStatement)sb.getStatement(0); + ForStatementBlock fsb = (ForStatementBlock) sb; + ForStatement fs = (ForStatement) sb.getStatement(0); ArrayList body = fs.getBody(); // step through stmt blocks in FOR stmt body - for (StatementBlock stmtBlock : body) + for(StatementBlock stmtBlock : body) ret |= constructLops(stmtBlock); // handle for stmt predicate - if (fsb.getFromHops() != null){ + if(fsb.getFromHops() != null) { Lop llobs = fsb.getFromHops().constructLops(); fsb.setFromLops(llobs); } - if (fsb.getToHops() != null){ + if(fsb.getToHops() != null) { Lop llobs = fsb.getToHops().constructLops(); fsb.setToLops(llobs); } - if (fsb.getIncrementHops() != null){ + if(fsb.getIncrementHops() != null) { Lop llobs = fsb.getIncrementHops().constructLops(); fsb.setIncrementLops(llobs); } ret |= fsb.updatePredicateRecompilationFlags(); } - else if (sb instanceof FunctionStatementBlock) { + else if(sb instanceof FunctionStatementBlock) { FunctionStatementBlock fsb = (FunctionStatementBlock) sb; - FunctionStatement functStmt = (FunctionStatement)sb.getStatement(0); + FunctionStatement functStmt = (FunctionStatement) sb.getStatement(0); ArrayList body = functStmt.getBody(); // step through stmt blocks in while stmt body - for( StatementBlock stmtBlock : body ) + for(StatementBlock stmtBlock : body) ret |= constructLops(stmtBlock); - if( fsb.isRecompileOnce() ) + if(fsb.isRecompileOnce()) fsb.setRecompileOnce(ret); } // handle default case for regular StatementBlock else { - if (sb.getHops() == null) + if(sb.getHops() == null) sb.setHops(new ArrayList()); ArrayList lops = new ArrayList<>(); - for (Hop hop : sb.getHops()) + for(Hop hop : sb.getHops()) lops.add(hop.constructLops()); sb.setLops(lops); ret |= sb.updateRecompilationFlag(); @@ -440,21 +431,20 @@ else if (sb instanceof FunctionStatementBlock) { return ret; } - public Program getRuntimeProgram(DMLProgram prog, DMLConfig config) - throws LanguageException, DMLRuntimeException, LopsException, HopsException - { + public Program getRuntimeProgram(DMLProgram prog, DMLConfig config) + throws LanguageException, DMLRuntimeException, LopsException, HopsException { // constructor resets the set of registered functions Program rtprog = new Program(prog); // for all namespaces, translate function statement blocks into function program blocks - for (String namespace : prog.getNamespaces().keySet()){ + for(String namespace : prog.getNamespaces().keySet()) { - for (String fname : prog.getFunctionStatementBlocks(namespace).keySet()){ + for(String fname : prog.getFunctionStatementBlocks(namespace).keySet()) { // add program block to program FunctionStatementBlock fsb = prog.getFunctionStatementBlocks(namespace).get(fname); prepareAndAddFunctionProgramBlock(rtprog, config, namespace, fname, fsb, true); // add unoptimized block to program (for second-order calls) - if( prog.getNamespaces().get(namespace).containsFunction(fname, false) ) { + if(prog.getNamespaces().get(namespace).containsFunction(fname, false)) { prepareAndAddFunctionProgramBlock(rtprog, config, namespace, fname, prog.getNamespaces().get(namespace).getFunction(fname, false), false); } @@ -462,25 +452,23 @@ public Program getRuntimeProgram(DMLProgram prog, DMLConfig config) } // translate all top-level statement blocks to program blocks - for (StatementBlock sb : prog.getStatementBlocks() ) { + for(StatementBlock sb : prog.getStatementBlocks()) { // add program block to program ProgramBlock rtpb = createRuntimeProgramBlock(rtprog, sb, config); rtprog.addProgramBlock(rtpb); } - //enhance runtime program by automatic operator fusion - if( ConfigurationManager.isCodegenEnabled() - && SpoofCompiler.INTEGRATION==IntegrationType.RUNTIME ){ + // enhance runtime program by automatic operator fusion + if(ConfigurationManager.isCodegenEnabled() && SpoofCompiler.INTEGRATION == IntegrationType.RUNTIME) { codgenHopsDAG(rtprog); } - return rtprog ; + return rtprog; } - private void prepareAndAddFunctionProgramBlock(Program rtprog, DMLConfig config, - String fnamespace, String fname, FunctionStatementBlock fsb, boolean opt) - { - FunctionProgramBlock rtpb = (FunctionProgramBlock)createRuntimeProgramBlock(rtprog, fsb, config); + private void prepareAndAddFunctionProgramBlock(Program rtprog, DMLConfig config, String fnamespace, String fname, + FunctionStatementBlock fsb, boolean opt) { + FunctionProgramBlock rtpb = (FunctionProgramBlock) createRuntimeProgramBlock(rtprog, fsb, config); rtprog.addFunctionProgramBlock(fnamespace, fname, rtpb, opt); rtpb.setRecompileOnce(fsb.isRecompileOnce()); rtpb.setNondeterministic(fsb.isNondeterministic()); @@ -496,7 +484,7 @@ public ProgramBlock createRuntimeProgramBlock(Program prog, StatementBlock sb, D ProgramBlock retPB = null; // process While Statement - add runtime program blocks to program - if (sb instanceof WhileStatementBlock){ + if(sb instanceof WhileStatementBlock) { // create DAG for loop predicates pred_dag = new Dag<>(); @@ -505,7 +493,7 @@ public ProgramBlock createRuntimeProgramBlock(Program prog, StatementBlock sb, D // create instructions for loop predicates pred_instruct = new ArrayList<>(); ArrayList pInst = pred_dag.getJobs(null, config); - for (Instruction i : pInst ) { + for(Instruction i : pInst) { pred_instruct.add(i); } @@ -514,9 +502,9 @@ public ProgramBlock createRuntimeProgramBlock(Program prog, StatementBlock sb, D //// process the body of the while statement block //// - WhileStatementBlock wsb = (WhileStatementBlock)sb; - WhileStatement wstmt = (WhileStatement)wsb.getStatement(0); - for (StatementBlock sblock : wstmt.getBody()){ + WhileStatementBlock wsb = (WhileStatementBlock) sb; + WhileStatement wstmt = (WhileStatement) wsb.getStatement(0); + for(StatementBlock sblock : wstmt.getBody()) { // process the body ProgramBlock childBlock = createRuntimeProgramBlock(prog, sblock, config); @@ -525,7 +513,7 @@ public ProgramBlock createRuntimeProgramBlock(Program prog, StatementBlock sb, D retPB = rtpb; - //post processing for generating missing instructions + // post processing for generating missing instructions retPB.setExitInstruction(deriveExitInstruction(sb)); // add statement block @@ -536,7 +524,7 @@ public ProgramBlock createRuntimeProgramBlock(Program prog, StatementBlock sb, D } // process If Statement - add runtime program blocks to program - else if (sb instanceof IfStatementBlock){ + else if(sb instanceof IfStatementBlock) { // create DAG for loop predicates pred_dag = new Dag<>(); @@ -545,7 +533,7 @@ else if (sb instanceof IfStatementBlock){ // create instructions for loop predicates pred_instruct = new ArrayList<>(); ArrayList pInst = pred_dag.getJobs(null, config); - for (Instruction i : pInst ) { + for(Instruction i : pInst) { pred_instruct.add(i); } @@ -553,24 +541,24 @@ else if (sb instanceof IfStatementBlock){ IfProgramBlock rtpb = new IfProgramBlock(prog, pred_instruct); // process the body of the if statement block - IfStatementBlock isb = (IfStatementBlock)sb; - IfStatement istmt = (IfStatement)isb.getStatement(0); + IfStatementBlock isb = (IfStatementBlock) sb; + IfStatement istmt = (IfStatement) isb.getStatement(0); // process the if body - for (StatementBlock sblock : istmt.getIfBody()){ + for(StatementBlock sblock : istmt.getIfBody()) { ProgramBlock childBlock = createRuntimeProgramBlock(prog, sblock, config); rtpb.addProgramBlockIfBody(childBlock); } // process the else body - for (StatementBlock sblock : istmt.getElseBody()){ + for(StatementBlock sblock : istmt.getElseBody()) { ProgramBlock childBlock = createRuntimeProgramBlock(prog, sblock, config); - rtpb.addProgramBlockElseBody(childBlock); + rtpb.addProgramBlockElseBody(childBlock); } retPB = rtpb; - //post processing for generating missing instructions + // post processing for generating missing instructions retPB.setExitInstruction(deriveExitInstruction(sb)); // add statement block @@ -582,19 +570,18 @@ else if (sb instanceof IfStatementBlock){ // process For Statement - add runtime program blocks to program // NOTE: applies to ForStatementBlock and ParForStatementBlock - else if (sb instanceof ForStatementBlock) - { + else if(sb instanceof ForStatementBlock) { ForStatementBlock fsb = (ForStatementBlock) sb; - // create DAGs for loop predicates + // create DAGs for loop predicates Dag fromDag = new Dag<>(); Dag toDag = new Dag<>(); Dag incrementDag = new Dag<>(); - if( fsb.getFromHops()!=null ) + if(fsb.getFromHops() != null) fsb.getFromLops().addToDag(fromDag); - if( fsb.getToHops()!=null ) + if(fsb.getToHops() != null) fsb.getToLops().addToDag(toDag); - if( fsb.getIncrementHops()!=null ) + if(fsb.getIncrementHops() != null) fsb.getIncrementLops().addToDag(incrementDag); // create instructions for loop predicates @@ -606,13 +593,13 @@ else if (sb instanceof ForStatementBlock) ForProgramBlock rtpb = null; IterablePredicate iterPred = fsb.getIterPredicate(); - if( sb instanceof ParForStatementBlock && ConfigurationManager.isParallelParFor() ) { - rtpb = new ParForProgramBlock(prog, iterPred.getIterVar().getName(), - iterPred.getParForParams(), ((ParForStatementBlock)sb).getResultVariables()); - ParForProgramBlock pfrtpb = (ParForProgramBlock)rtpb; - pfrtpb.setStatementBlock(sb); //used for optimization and creating unscoped variables + if(sb instanceof ParForStatementBlock && ConfigurationManager.isParallelParFor()) { + rtpb = new ParForProgramBlock(prog, iterPred.getIterVar().getName(), iterPred.getParForParams(), + ((ParForStatementBlock) sb).getResultVariables()); + ParForProgramBlock pfrtpb = (ParForProgramBlock) rtpb; + pfrtpb.setStatementBlock(sb); // used for optimization and creating unscoped variables } - else {//ForStatementBlock + else {// ForStatementBlock rtpb = new ForProgramBlock(prog, iterPred.getIterVar().getName()); } @@ -621,15 +608,15 @@ else if (sb instanceof ForStatementBlock) rtpb.setIncrementInstructions(incrementInstructions); // process the body of the for statement block - ForStatement fs = (ForStatement)fsb.getStatement(0); - for (StatementBlock sblock : fs.getBody()){ + ForStatement fs = (ForStatement) fsb.getStatement(0); + for(StatementBlock sblock : fs.getBody()) { ProgramBlock childBlock = createRuntimeProgramBlock(prog, sblock, config); - rtpb.addProgramBlock(childBlock); + rtpb.addProgramBlock(childBlock); } retPB = rtpb; - //post processing for generating missing instructions + // post processing for generating missing instructions retPB.setExitInstruction(deriveExitInstruction(sb)); // add statement block @@ -640,24 +627,24 @@ else if (sb instanceof ForStatementBlock) } // process function statement block - add runtime program blocks to program - else if (sb instanceof FunctionStatementBlock){ + else if(sb instanceof FunctionStatementBlock) { - FunctionStatementBlock fsb = (FunctionStatementBlock)sb; - FunctionStatement fstmt = (FunctionStatement)fsb.getStatement(0); + FunctionStatementBlock fsb = (FunctionStatementBlock) sb; + FunctionStatement fstmt = (FunctionStatement) fsb.getStatement(0); FunctionProgramBlock rtpb = null; // create function program block rtpb = new FunctionProgramBlock(prog, fstmt.getInputParams(), fstmt.getOutputParams()); // process the function statement body - for (StatementBlock sblock : fstmt.getBody()){ + for(StatementBlock sblock : fstmt.getBody()) { // process the body ProgramBlock childBlock = createRuntimeProgramBlock(prog, sblock, config); rtpb.addProgramBlock(childBlock); } // check there are actually Lops in to process (loop stmt body will not have any) - if (fsb.getLops() != null && !fsb.getLops().isEmpty()){ + if(fsb.getLops() != null && !fsb.getLops().isEmpty()) { throw new LopsException(fsb.printBlockErrorLocation() + "FunctionStatementBlock should have no Lops"); } @@ -678,9 +665,9 @@ else if (sb instanceof FunctionStatementBlock){ dag = new Dag<>(); // check there are actually Lops in to process (loop stmt body will not have any) - if (sb.getLops() != null && !sb.getLops().isEmpty()){ + if(sb.getLops() != null && !sb.getLops().isEmpty()) { - for (Lop l : sb.getLops()) { + for(Lop l : sb.getLops()) { l.addToDag(dag); } @@ -691,8 +678,8 @@ else if (sb instanceof FunctionStatementBlock){ retPB = rtpb; - //post processing for generating missing instructions - //retPB.setExitInstruction(deriveExitInstruction(sb)); + // post processing for generating missing instructions + // retPB.setExitInstruction(deriveExitInstruction(sb)); // add statement block retPB.setStatementBlock(sb); @@ -707,90 +694,89 @@ else if (sb instanceof FunctionStatementBlock){ public static void refreshMemEstimates(DMLProgram dmlp) { // for each namespace, handle function program blocks -- forward direction - for (String namespaceKey : dmlp.getNamespaces().keySet()){ - for (String fname : dmlp.getFunctionStatementBlocks(namespaceKey).keySet()){ + for(String namespaceKey : dmlp.getNamespaces().keySet()) { + for(String fname : dmlp.getFunctionStatementBlocks(namespaceKey).keySet()) { FunctionStatementBlock fsblock = dmlp.getFunctionStatementBlock(namespaceKey, fname); refreshMemEstimates(fsblock); } } // handle statement blocks in "main" method - for (int i = 0; i < dmlp.getNumStatementBlocks(); i++) { + for(int i = 0; i < dmlp.getNumStatementBlocks(); i++) { StatementBlock current = dmlp.getStatementBlock(i); refreshMemEstimates(current); } } private static Instruction deriveExitInstruction(StatementBlock sb) { - Set rmVars = VariableSet.union( - VariableSet.minus(sb.liveIn(), sb.liveOut()), - VariableSet.minus(sb.getKill(), sb.liveOut())).getVariableNames(); - return rmVars.isEmpty() ? null : - VariableCPInstruction.prepareRemoveInstruction(rmVars.toArray(new String[0])); + Set rmVars = VariableSet + .union(VariableSet.minus(sb.liveIn(), sb.liveOut()), VariableSet.minus(sb.getKill(), sb.liveOut())) + .getVariableNames(); + return rmVars.isEmpty() ? null : VariableCPInstruction.prepareRemoveInstruction(rmVars.toArray(new String[0])); } public static void refreshMemEstimates(StatementBlock current) { MemoTable memo = new MemoTable(); - if( HopRewriteUtils.isLastLevelStatementBlock(current) ) { + if(HopRewriteUtils.isLastLevelStatementBlock(current)) { ArrayList hopsDAG = current.getHops(); - if (hopsDAG != null && !hopsDAG.isEmpty()) - for( Hop hop : hopsDAG ) + if(hopsDAG != null && !hopsDAG.isEmpty()) + for(Hop hop : hopsDAG) hop.refreshMemEstimates(memo); } - if (current instanceof FunctionStatementBlock) { + if(current instanceof FunctionStatementBlock) { - FunctionStatement fstmt = (FunctionStatement)current.getStatement(0); - for (StatementBlock sb : fstmt.getBody()){ + FunctionStatement fstmt = (FunctionStatement) current.getStatement(0); + for(StatementBlock sb : fstmt.getBody()) { refreshMemEstimates(sb); } } - else if (current instanceof WhileStatementBlock) { + else if(current instanceof WhileStatementBlock) { // handle predicate WhileStatementBlock wstb = (WhileStatementBlock) current; wstb.getPredicateHops().refreshMemEstimates(new MemoTable()); - if (wstb.getNumStatements() > 1) + if(wstb.getNumStatements() > 1) LOG.debug("While statement block has more than 1 stmt"); - WhileStatement ws = (WhileStatement)wstb.getStatement(0); + WhileStatement ws = (WhileStatement) wstb.getStatement(0); - for (StatementBlock sb : ws.getBody()){ + for(StatementBlock sb : ws.getBody()) { refreshMemEstimates(sb); } } - else if (current instanceof IfStatementBlock) { + else if(current instanceof IfStatementBlock) { // handle predicate IfStatementBlock istb = (IfStatementBlock) current; istb.getPredicateHops().refreshMemEstimates(new MemoTable()); - if (istb.getNumStatements() > 1) + if(istb.getNumStatements() > 1) LOG.debug("If statement block has more than 1 stmt"); - IfStatement is = (IfStatement)istb.getStatement(0); + IfStatement is = (IfStatement) istb.getStatement(0); - for (StatementBlock sb : is.getIfBody()){ + for(StatementBlock sb : is.getIfBody()) { refreshMemEstimates(sb); } - for (StatementBlock sb : is.getElseBody()){ + for(StatementBlock sb : is.getElseBody()) { refreshMemEstimates(sb); } } - else if (current instanceof ForStatementBlock) { + else if(current instanceof ForStatementBlock) { // handle predicate ForStatementBlock fsb = (ForStatementBlock) current; - if (fsb.getFromHops() != null) + if(fsb.getFromHops() != null) fsb.getFromHops().refreshMemEstimates(new MemoTable()); - if (fsb.getToHops() != null) + if(fsb.getToHops() != null) fsb.getToHops().refreshMemEstimates(new MemoTable()); - if (fsb.getIncrementHops() != null) + if(fsb.getIncrementHops() != null) fsb.getIncrementHops().refreshMemEstimates(new MemoTable()); - if (fsb.getNumStatements() > 1) + if(fsb.getNumStatements() > 1) LOG.debug("For statement block has more than 1 stmt"); - ForStatement ws = (ForStatement)fsb.getStatement(0); + ForStatement ws = (ForStatement) fsb.getStatement(0); - for (StatementBlock sb : ws.getBody()){ + for(StatementBlock sb : ws.getBody()) { refreshMemEstimates(sb); } } @@ -799,15 +785,15 @@ else if (current instanceof ForStatementBlock) { public static void resetHopsDAGVisitStatus(DMLProgram dmlp) { // for each namespace, handle function program blocks -- forward direction - for (String namespaceKey : dmlp.getNamespaces().keySet()){ - for (String fname : dmlp.getFunctionStatementBlocks(namespaceKey).keySet()){ + for(String namespaceKey : dmlp.getNamespaces().keySet()) { + for(String fname : dmlp.getFunctionStatementBlocks(namespaceKey).keySet()) { FunctionStatementBlock fsblock = dmlp.getFunctionStatementBlock(namespaceKey, fname); resetHopsDAGVisitStatus(fsblock); } } // handle statement blocks in "main" method - for (int i = 0; i < dmlp.getNumStatementBlocks(); i++) { + for(int i = 0; i < dmlp.getNumStatementBlocks(); i++) { StatementBlock current = dmlp.getStatementBlock(i); resetHopsDAGVisitStatus(current); } @@ -815,54 +801,54 @@ public static void resetHopsDAGVisitStatus(DMLProgram dmlp) { public static void resetHopsDAGVisitStatus(StatementBlock current) { - if( HopRewriteUtils.isLastLevelStatementBlock(current) ) { + if(HopRewriteUtils.isLastLevelStatementBlock(current)) { ArrayList hopsDAG = current.getHops(); - if (hopsDAG != null && !hopsDAG.isEmpty() ) { + if(hopsDAG != null && !hopsDAG.isEmpty()) { Hop.resetVisitStatus(hopsDAG); } } - if (current instanceof FunctionStatementBlock) { - FunctionStatement fstmt = (FunctionStatement)current.getStatement(0); - for (StatementBlock sb : fstmt.getBody()){ + if(current instanceof FunctionStatementBlock) { + FunctionStatement fstmt = (FunctionStatement) current.getStatement(0); + for(StatementBlock sb : fstmt.getBody()) { resetHopsDAGVisitStatus(sb); } } - else if (current instanceof WhileStatementBlock) { + else if(current instanceof WhileStatementBlock) { // handle predicate WhileStatementBlock wstb = (WhileStatementBlock) current; wstb.getPredicateHops().resetVisitStatus(); - WhileStatement ws = (WhileStatement)wstb.getStatement(0); - for (StatementBlock sb : ws.getBody()) + WhileStatement ws = (WhileStatement) wstb.getStatement(0); + for(StatementBlock sb : ws.getBody()) resetHopsDAGVisitStatus(sb); } - else if (current instanceof IfStatementBlock) { + else if(current instanceof IfStatementBlock) { // handle predicate IfStatementBlock istb = (IfStatementBlock) current; istb.getPredicateHops().resetVisitStatus(); - IfStatement is = (IfStatement)istb.getStatement(0); - for (StatementBlock sb : is.getIfBody()) + IfStatement is = (IfStatement) istb.getStatement(0); + for(StatementBlock sb : is.getIfBody()) resetHopsDAGVisitStatus(sb); - for (StatementBlock sb : is.getElseBody()) + for(StatementBlock sb : is.getElseBody()) resetHopsDAGVisitStatus(sb); } - else if (current instanceof ForStatementBlock) { + else if(current instanceof ForStatementBlock) { // handle predicate ForStatementBlock fsb = (ForStatementBlock) current; - if (fsb.getFromHops() != null) + if(fsb.getFromHops() != null) fsb.getFromHops().resetVisitStatus(); - if (fsb.getToHops() != null) + if(fsb.getToHops() != null) fsb.getToHops().resetVisitStatus(); - if (fsb.getIncrementHops() != null) + if(fsb.getIncrementHops() != null) fsb.getIncrementHops().resetVisitStatus(); - if (fsb.getNumStatements() > 1) + if(fsb.getNumStatements() > 1) LOG.debug("For statment block has more than 1 stmt"); - ForStatement ws = (ForStatement)fsb.getStatement(0); + ForStatement ws = (ForStatement) fsb.getStatement(0); - for (StatementBlock sb : ws.getBody()){ + for(StatementBlock sb : ws.getBody()) { resetHopsDAGVisitStatus(sb); } } @@ -871,14 +857,14 @@ else if (current instanceof ForStatementBlock) { public void resetLopsDAGVisitStatus(DMLProgram dmlp) { // for each namespace, handle function program blocks - for (String namespaceKey : dmlp.getNamespaces().keySet()){ - for (String fname : dmlp.getFunctionStatementBlocks(namespaceKey).keySet()){ + for(String namespaceKey : dmlp.getNamespaces().keySet()) { + for(String fname : dmlp.getFunctionStatementBlocks(namespaceKey).keySet()) { FunctionStatementBlock fsblock = dmlp.getFunctionStatementBlock(namespaceKey, fname); resetLopsDAGVisitStatus(fsblock); } } - for (int i = 0; i < dmlp.getNumStatementBlocks(); i++) { + for(int i = 0; i < dmlp.getNumStatementBlocks(); i++) { StatementBlock current = dmlp.getStatementBlock(i); resetLopsDAGVisitStatus(current); } @@ -888,88 +874,88 @@ public void resetLopsDAGVisitStatus(StatementBlock current) { ArrayList hopsDAG = current.getHops(); - if (hopsDAG != null && !hopsDAG.isEmpty() ) { + if(hopsDAG != null && !hopsDAG.isEmpty()) { Iterator iter = hopsDAG.iterator(); - while (iter.hasNext()){ + while(iter.hasNext()) { Hop currentHop = iter.next(); currentHop.getLops().resetVisitStatus(); } } - if (current instanceof FunctionStatementBlock) { + if(current instanceof FunctionStatementBlock) { FunctionStatementBlock fsb = (FunctionStatementBlock) current; - FunctionStatement fs = (FunctionStatement)fsb.getStatement(0); + FunctionStatement fs = (FunctionStatement) fsb.getStatement(0); - for (StatementBlock sb : fs.getBody()){ + for(StatementBlock sb : fs.getBody()) { resetLopsDAGVisitStatus(sb); } } - if (current instanceof WhileStatementBlock) { + if(current instanceof WhileStatementBlock) { WhileStatementBlock wstb = (WhileStatementBlock) current; wstb.getPredicateLops().resetVisitStatus(); - if (wstb.getNumStatements() > 1) + if(wstb.getNumStatements() > 1) LOG.debug("While statement block has more than 1 stmt"); - WhileStatement ws = (WhileStatement)wstb.getStatement(0); + WhileStatement ws = (WhileStatement) wstb.getStatement(0); - for (StatementBlock sb : ws.getBody()){ + for(StatementBlock sb : ws.getBody()) { resetLopsDAGVisitStatus(sb); } } - if (current instanceof IfStatementBlock) { + if(current instanceof IfStatementBlock) { IfStatementBlock istb = (IfStatementBlock) current; istb.getPredicateLops().resetVisitStatus(); - if (istb.getNumStatements() > 1) + if(istb.getNumStatements() > 1) LOG.debug("If statement block has more than 1 stmt"); - IfStatement is = (IfStatement)istb.getStatement(0); + IfStatement is = (IfStatement) istb.getStatement(0); - for (StatementBlock sb : is.getIfBody()){ + for(StatementBlock sb : is.getIfBody()) { resetLopsDAGVisitStatus(sb); } - for (StatementBlock sb : is.getElseBody()){ + for(StatementBlock sb : is.getElseBody()) { resetLopsDAGVisitStatus(sb); } } - if (current instanceof ForStatementBlock) { + if(current instanceof ForStatementBlock) { ForStatementBlock fsb = (ForStatementBlock) current; - if (fsb.getFromLops() != null) + if(fsb.getFromLops() != null) fsb.getFromLops().resetVisitStatus(); - if (fsb.getToLops() != null) + if(fsb.getToLops() != null) fsb.getToLops().resetVisitStatus(); - if (fsb.getIncrementLops() != null) + if(fsb.getIncrementLops() != null) fsb.getIncrementLops().resetVisitStatus(); - if (fsb.getNumStatements() > 1) + if(fsb.getNumStatements() > 1) LOG.debug("For statement block has more than 1 stmt"); - ForStatement ws = (ForStatement)fsb.getStatement(0); + ForStatement ws = (ForStatement) fsb.getStatement(0); - for (StatementBlock sb : ws.getBody()){ + for(StatementBlock sb : ws.getBody()) { resetLopsDAGVisitStatus(sb); } } } public void constructHops(StatementBlock sb) { - if (sb instanceof WhileStatementBlock) { + if(sb instanceof WhileStatementBlock) { constructHopsForWhileControlBlock((WhileStatementBlock) sb); return; } - if (sb instanceof IfStatementBlock) { + if(sb instanceof IfStatementBlock) { constructHopsForIfControlBlock((IfStatementBlock) sb); return; } - if (sb instanceof ForStatementBlock) { //incl ParForStatementBlock + if(sb instanceof ForStatementBlock) { // incl ParForStatementBlock constructHopsForForControlBlock((ForStatementBlock) sb); return; } - if (sb instanceof FunctionStatementBlock) { + if(sb instanceof FunctionStatementBlock) { constructHopsForFunctionControlBlock((FunctionStatementBlock) sb); return; } @@ -977,76 +963,80 @@ public void constructHops(StatementBlock sb) { HashMap ids = new HashMap<>(); ArrayList output = new ArrayList<>(); - VariableSet liveIn = sb.liveIn(); + VariableSet liveIn = sb.liveIn(); VariableSet liveOut = sb.liveOut(); VariableSet updated = sb._updated; - VariableSet gen = sb._gen; + VariableSet gen = sb._gen; VariableSet updatedLiveOut = new VariableSet(); // handle liveout variables that are updated --> target identifiers for Assignment HashMap liveOutToTemp = new HashMap<>(); - for (int i = 0; i < sb.getNumStatements(); i++) { + for(int i = 0; i < sb.getNumStatements(); i++) { Statement current = sb.getStatement(i); - if (current instanceof AssignmentStatement) { + if(current instanceof AssignmentStatement) { AssignmentStatement as = (AssignmentStatement) current; DataIdentifier target = as.getTarget(); - if (target != null) { - if (liveOut.containsVariable(target.getName())) { + if(target != null) { + if(liveOut.containsVariable(target.getName())) { liveOutToTemp.put(target.getName(), Integer.valueOf(i)); } } } - if (current instanceof MultiAssignmentStatement) { + if(current instanceof MultiAssignmentStatement) { MultiAssignmentStatement mas = (MultiAssignmentStatement) current; - for (DataIdentifier target : mas.getTargetList()){ - if (liveOut.containsVariable(target.getName())) { + for(DataIdentifier target : mas.getTargetList()) { + if(liveOut.containsVariable(target.getName())) { liveOutToTemp.put(target.getName(), Integer.valueOf(i)); } } } } - // only create transient read operations for variables either updated or read-before-update - // (i.e., from LV analysis, updated and gen sets) - if ( !liveIn.getVariables().values().isEmpty() ) { + // only create transient read operations for variables either updated or read-before-update + // (i.e., from LV analysis, updated and gen sets) + if(!liveIn.getVariables().values().isEmpty()) { - for (String varName : liveIn.getVariables().keySet()) { + for(String varName : liveIn.getVariables().keySet()) { - if (updated.containsVariable(varName) || gen.containsVariable(varName)){ + if(updated.containsVariable(varName) || gen.containsVariable(varName)) { DataIdentifier var = liveIn.getVariables().get(varName); - long actualDim1 = (var instanceof IndexedIdentifier) ? ((IndexedIdentifier)var).getOrigDim1() : var.getDim1(); - long actualDim2 = (var instanceof IndexedIdentifier) ? ((IndexedIdentifier)var).getOrigDim2() : var.getDim2(); - DataOp read = new DataOp(var.getName(), var.getDataType(), var.getValueType(), OpOpData.TRANSIENTREAD, null, actualDim1, actualDim2, var.getNnz(), var.getBlocksize()); + long actualDim1 = (var instanceof IndexedIdentifier) ? ((IndexedIdentifier) var).getOrigDim1() : var + .getDim1(); + long actualDim2 = (var instanceof IndexedIdentifier) ? ((IndexedIdentifier) var).getOrigDim2() : var + .getDim2(); + DataOp read = new DataOp(var.getName(), var.getDataType(), var.getValueType(), + OpOpData.TRANSIENTREAD, null, actualDim1, actualDim2, var.getNnz(), var.getBlocksize()); read.setParseInfo(var); ids.put(varName, read); } } } - for( int i = 0; i < sb.getNumStatements(); i++ ) { + for(int i = 0; i < sb.getNumStatements(); i++) { Statement current = sb.getStatement(i); - if (current instanceof OutputStatement) { + if(current instanceof OutputStatement) { OutputStatement os = (OutputStatement) current; DataExpression source = os.getSource(); DataIdentifier target = os.getIdentifier(); - //error handling unsupported indexing expression in write statement - if( target instanceof IndexedIdentifier ) { - throw new LanguageException(source.printErrorLocation()+": Unsupported indexing expression in write statement. " + - "Please, assign the right indexing result to a variable and write this variable."); + // error handling unsupported indexing expression in write statement + if(target instanceof IndexedIdentifier) { + throw new LanguageException( + source.printErrorLocation() + ": Unsupported indexing expression in write statement. " + + "Please, assign the right indexing result to a variable and write this variable."); } - DataOp ae = (DataOp)processExpression(source, target, ids); + DataOp ae = (DataOp) processExpression(source, target, ids); Expression fmtExpr = os.getExprParam(DataExpression.FORMAT_TYPE); - ae.setFileFormat((fmtExpr instanceof StringIdentifier) ? - Expression.convertFormatType(fmtExpr.toString()) : FileFormat.UNKNOWN); + ae.setFileFormat((fmtExpr instanceof StringIdentifier) ? Expression + .convertFormatType(fmtExpr.toString()) : FileFormat.UNKNOWN); - if (ae.getDataType() == DataType.SCALAR ) { + if(ae.getDataType() == DataType.SCALAR) { ae.setOutputParams(ae.getDim1(), ae.getDim2(), ae.getNnz(), ae.getUpdateType(), -1); } else { @@ -1063,7 +1053,8 @@ public void constructHops(StatementBlock sb) { case COMPRESSED: case UNKNOWN: // write output in binary block format - ae.setOutputParams(ae.getDim1(), ae.getDim2(), ae.getNnz(), ae.getUpdateType(), ae.getBlocksize()); + ae.setOutputParams(ae.getDim1(), ae.getDim2(), ae.getNnz(), ae.getUpdateType(), + ae.getBlocksize()); break; case FEDERATED: ae.setOutputParams(ae.getDim1(), ae.getDim2(), -1, ae.getUpdateType(), -1); @@ -1076,7 +1067,7 @@ public void constructHops(StatementBlock sb) { output.add(ae); } - if (current instanceof PrintStatement) { + if(current instanceof PrintStatement) { DataIdentifier target = createTarget(); target.setDataType(DataType.SCALAR); target.setValueType(ValueType.STRING); @@ -1086,66 +1077,72 @@ public void constructHops(StatementBlock sb) { PRINTTYPE ptype = ps.getType(); try { - if (ptype == PRINTTYPE.PRINT) { + if(ptype == PRINTTYPE.PRINT) { OpOp1 op = OpOp1.PRINT; Expression source = ps.getExpressions().get(0); Hop ae = processExpression(source, target, ids); - Hop printHop = new UnaryOp(target.getName(), target.getDataType(), target.getValueType(), op, ae); + Hop printHop = new UnaryOp(target.getName(), target.getDataType(), target.getValueType(), op, + ae); printHop.setParseInfo(current); output.add(printHop); } - else if (ptype == PRINTTYPE.ASSERT) { + else if(ptype == PRINTTYPE.ASSERT) { OpOp1 op = OpOp1.ASSERT; Expression source = ps.getExpressions().get(0); Hop ae = processExpression(source, target, ids); - Hop printHop = new UnaryOp(target.getName(), target.getDataType(), target.getValueType(), op, ae); + Hop printHop = new UnaryOp(target.getName(), target.getDataType(), target.getValueType(), op, + ae); printHop.setParseInfo(current); output.add(printHop); } - else if (ptype == PRINTTYPE.STOP) { + else if(ptype == PRINTTYPE.STOP) { OpOp1 op = OpOp1.STOP; Expression source = ps.getExpressions().get(0); Hop ae = processExpression(source, target, ids); - Hop stopHop = new UnaryOp(target.getName(), target.getDataType(), target.getValueType(), op, ae); + Hop stopHop = new UnaryOp(target.getName(), target.getDataType(), target.getValueType(), op, + ae); stopHop.setParseInfo(current); output.add(stopHop); - sb.setSplitDag(true); //avoid merge - } else if (ptype == PRINTTYPE.PRINTF) { + sb.setSplitDag(true); // avoid merge + } + else if(ptype == PRINTTYPE.PRINTF) { List expressions = ps.getExpressions(); Hop[] inHops = new Hop[expressions.size()]; // process the expressions (function parameters) that // make up the printf-styled print statement // into Hops so that these can be passed to the printf // Hop (ie, MultipleOp) as input Hops - for (int j = 0; j < expressions.size(); j++) { + for(int j = 0; j < expressions.size(); j++) { Hop inHop = processExpression(expressions.get(j), target, ids); inHops[j] = inHop; } target.setValueType(ValueType.STRING); - Hop printfHop = new NaryOp(target.getName(), target.getDataType(), - target.getValueType(), OpOpN.PRINTF, inHops); + Hop printfHop = new NaryOp(target.getName(), target.getDataType(), target.getValueType(), + OpOpN.PRINTF, inHops); output.add(printfHop); } - } catch (HopsException e) { + } + catch(HopsException e) { throw new LanguageException(e); } } - if (current instanceof AssignmentStatement) { + if(current instanceof AssignmentStatement) { AssignmentStatement as = (AssignmentStatement) current; DataIdentifier target = as.getTarget(); Expression source = as.getSource(); - // CASE: regular assignment statement -- source is DML expression that is NOT user-defined or external function - if (!(source instanceof FunctionCallIdentifier)){ + // CASE: regular assignment statement -- source is DML expression that is NOT user-defined or external + // function + if(!(source instanceof FunctionCallIdentifier)) { // CASE: target is regular data identifier - if (!(target instanceof IndexedIdentifier)) { - //process right hand side and accumulation + if(!(target instanceof IndexedIdentifier)) { + // process right hand side and accumulation Hop ae = processExpression(source, target, ids); - if( as.isAccumulator() ) { + if(as.isAccumulator()) { DataIdentifier accum = getAccumulatorData(liveIn, target.getName()); ae = HopRewriteUtils.createBinary(ids.get(target.getName()), ae, OpOp2.PLUS); target.setProperties(accum.getOutput()); @@ -1153,31 +1150,33 @@ else if (ptype == PRINTTYPE.STOP) { else target.setProperties(source.getOutput()); - if (source instanceof BuiltinFunctionExpression){ - BuiltinFunctionExpression BuiltinSource = (BuiltinFunctionExpression)source; - if (BuiltinSource.getOpCode() == Builtins.TIME) + if(source instanceof BuiltinFunctionExpression) { + BuiltinFunctionExpression BuiltinSource = (BuiltinFunctionExpression) source; + if(BuiltinSource.getOpCode() == Builtins.TIME) sb.setSplitDag(true); } ids.put(target.getName(), ae); - //add transient write if needed + // add transient write if needed Integer statementId = liveOutToTemp.get(target.getName()); - if ((statementId != null) && (statementId.intValue() == i)) { - DataOp transientwrite = new DataOp(target.getName(), target.getDataType(), target.getValueType(), ae, OpOpData.TRANSIENTWRITE, null); - transientwrite.setOutputParams(ae.getDim1(), ae.getDim2(), ae.getNnz(), ae.getUpdateType(), ae.getBlocksize()); + if((statementId != null) && (statementId.intValue() == i)) { + DataOp transientwrite = new DataOp(target.getName(), target.getDataType(), + target.getValueType(), ae, OpOpData.TRANSIENTWRITE, null); + transientwrite.setOutputParams(ae.getDim1(), ae.getDim2(), ae.getNnz(), ae.getUpdateType(), + ae.getBlocksize()); transientwrite.setParseInfo(target); updatedLiveOut.addVariable(target.getName(), target); output.add(transientwrite); } - } + } // CASE: target is indexed identifier (left-hand side indexed expression) else { - Hop ae = processLeftIndexedExpression(source, (IndexedIdentifier)target, ids); + Hop ae = processLeftIndexedExpression(source, (IndexedIdentifier) target, ids); - if( as.isAccumulator() ) { + if(as.isAccumulator()) { DataIdentifier accum = getAccumulatorData(liveIn, target.getName()); - Hop rix = processIndexingExpression((IndexedIdentifier)target, null, ids); + Hop rix = processIndexingExpression((IndexedIdentifier) target, null, ids); Hop rhs = processExpression(source, null, ids); Hop binary = HopRewriteUtils.createBinary(rix, rhs, OpOp2.PLUS); HopRewriteUtils.replaceChildReference(ae, ae.getInput(1), binary); @@ -1187,126 +1186,133 @@ else if (ptype == PRINTTYPE.STOP) { ids.put(target.getName(), ae); // obtain origDim values BEFORE they are potentially updated during setProperties call - // (this is incorrect for LHS Indexing) - long origDim1 = ((IndexedIdentifier)target).getOrigDim1(); - long origDim2 = ((IndexedIdentifier)target).getOrigDim2(); + // (this is incorrect for LHS Indexing) + long origDim1 = ((IndexedIdentifier) target).getOrigDim1(); + long origDim2 = ((IndexedIdentifier) target).getOrigDim2(); target.setProperties(source.getOutput()); - ((IndexedIdentifier)target).setOriginalDimensions(origDim1, origDim2); + ((IndexedIdentifier) target).setOriginalDimensions(origDim1, origDim2); // preserve data type matrix of any index identifier // (required for scalar input to left indexing) - if( target.getDataType() != DataType.MATRIX ) { + if(target.getDataType() != DataType.MATRIX) { target.setDataType(DataType.MATRIX); target.setValueType(ValueType.FP64); target.setBlocksize(ConfigurationManager.getBlocksize()); } Integer statementId = liveOutToTemp.get(target.getName()); - if ((statementId != null) && (statementId.intValue() == i)) { - DataOp transientwrite = new DataOp(target.getName(), target.getDataType(), target.getValueType(), ae, OpOpData.TRANSIENTWRITE, null); - transientwrite.setOutputParams(origDim1, origDim2, ae.getNnz(), ae.getUpdateType(), ae.getBlocksize()); + if((statementId != null) && (statementId.intValue() == i)) { + DataOp transientwrite = new DataOp(target.getName(), target.getDataType(), + target.getValueType(), ae, OpOpData.TRANSIENTWRITE, null); + transientwrite.setOutputParams(origDim1, origDim2, ae.getNnz(), ae.getUpdateType(), + ae.getBlocksize()); transientwrite.setParseInfo(target); updatedLiveOut.addVariable(target.getName(), target); output.add(transientwrite); } } } - else - { - //assignment, function call + else { + // assignment, function call FunctionCallIdentifier fci = (FunctionCallIdentifier) source; - FunctionStatementBlock fsb = this._dmlProg.getFunctionStatementBlock(fci.getNamespace(),fci.getName()); + FunctionStatementBlock fsb = this._dmlProg.getFunctionStatementBlock(fci.getNamespace(), + fci.getName()); - //error handling missing function - if (fsb == null) { - throw new LanguageException(source.printErrorLocation() + "function " - + fci.getName() + " is undefined in namespace " + fci.getNamespace()); + // error handling missing function + if(fsb == null) { + throw new LanguageException(source.printErrorLocation() + "function " + fci.getName() + + " is undefined in namespace " + fci.getNamespace()); } - FunctionStatement fstmt = (FunctionStatement)fsb.getStatement(0); - String fkey = DMLProgram.constructFunctionKey(fci.getNamespace(),fci.getName()); + FunctionStatement fstmt = (FunctionStatement) fsb.getStatement(0); + String fkey = DMLProgram.constructFunctionKey(fci.getNamespace(), fci.getName()); - //error handling unsupported function call in indexing expression - if( target instanceof IndexedIdentifier ) { - throw new LanguageException("Unsupported function call to '"+fkey+"' in left indexing " + // error handling unsupported function call in indexing expression + if(target instanceof IndexedIdentifier) { + throw new LanguageException("Unsupported function call to '" + fkey + "' in left indexing " + "expression. Please, assign the function output to a variable."); } - //prepare function input names and inputs - List inputNames = new ArrayList<>(fci.getParamExprs().stream() - .map(e -> e.getName()).collect(Collectors.toList())); + // prepare function input names and inputs + List inputNames = new ArrayList<>( + fci.getParamExprs().stream().map(e -> e.getName()).collect(Collectors.toList())); List finputs = new ArrayList<>(fci.getParamExprs().stream() .map(e -> processExpression(e.getExpr(), null, ids)).collect(Collectors.toList())); - //append default expression for missing arguments + // append default expression for missing arguments appendDefaultArguments(fstmt, inputNames, finputs, ids); - //use function signature to obtain names for unnamed args - //(note: consistent parameters already checked for functions in general) - if( inputNames.stream().allMatch(n -> n==null) ) + // use function signature to obtain names for unnamed args + // (note: consistent parameters already checked for functions in general) + if(inputNames.stream().allMatch(n -> n == null)) inputNames = fstmt._inputParams.stream().map(d -> d.getName()).collect(Collectors.toList()); - //create function op + // create function op String[] inputNames2 = inputNames.toArray(new String[0]); FunctionType ftype = fsb.getFunctionOpType(); - FunctionOp fcall = (target == null) ? - new FunctionOp(ftype, fci.getNamespace(), fci.getName(), inputNames2, finputs, new String[]{}, false) : - new FunctionOp(ftype, fci.getNamespace(), fci.getName(), inputNames2, finputs, new String[]{target.getName()}, false); + FunctionOp fcall = (target == null) ? new FunctionOp(ftype, fci.getNamespace(), fci.getName(), + inputNames2, finputs, new String[] {}, false) : new FunctionOp(ftype, fci.getNamespace(), + fci.getName(), inputNames2, finputs, new String[] {target.getName()}, false); fcall.setParseInfo(fci); output.add(fcall); } } - else if (current instanceof MultiAssignmentStatement) { - //multi-assignment, by definition a function call + else if(current instanceof MultiAssignmentStatement) { + // multi-assignment, by definition a function call MultiAssignmentStatement mas = (MultiAssignmentStatement) current; Expression source = mas.getSource(); - if ( source instanceof FunctionCallIdentifier ) { + if(source instanceof FunctionCallIdentifier) { FunctionCallIdentifier fci = (FunctionCallIdentifier) source; - FunctionStatementBlock fsb = this._dmlProg.getFunctionStatementBlock(fci.getNamespace(),fci.getName()); - if (fsb == null){ - throw new LanguageException(source.printErrorLocation() + "function " - + fci.getName() + " is undefined in namespace " + fci.getNamespace()); + FunctionStatementBlock fsb = this._dmlProg.getFunctionStatementBlock(fci.getNamespace(), + fci.getName()); + if(fsb == null) { + throw new LanguageException(source.printErrorLocation() + "function " + fci.getName() + + " is undefined in namespace " + fci.getNamespace()); } - FunctionStatement fstmt = (FunctionStatement)fsb.getStatement(0); + FunctionStatement fstmt = (FunctionStatement) fsb.getStatement(0); - //prepare function input names and inputs - List inputNames = new ArrayList<>(fci.getParamExprs().stream() - .map(e -> e.getName()).collect(Collectors.toList())); + // prepare function input names and inputs + List inputNames = new ArrayList<>( + fci.getParamExprs().stream().map(e -> e.getName()).collect(Collectors.toList())); List finputs = new ArrayList<>(fci.getParamExprs().stream() .map(e -> processExpression(e.getExpr(), null, ids)).collect(Collectors.toList())); - //use function signature to obtain names for unnamed args - //(note: consistent parameters already checked for functions in general) - if( inputNames.stream().allMatch(n -> n==null) ) + // use function signature to obtain names for unnamed args + // (note: consistent parameters already checked for functions in general) + if(inputNames.stream().allMatch(n -> n == null)) inputNames = fstmt._inputParams.stream().map(d -> d.getName()).collect(Collectors.toList()); - //append default expression for missing arguments + // append default expression for missing arguments appendDefaultArguments(fstmt, inputNames, finputs, ids); - //create function op - String[] foutputs = mas.getTargetList().stream() - .map(d -> d.getName()).toArray(String[]::new); + // create function op + String[] foutputs = mas.getTargetList().stream().map(d -> d.getName()).toArray(String[]::new); FunctionType ftype = fsb.getFunctionOpType(); FunctionOp fcall = new FunctionOp(ftype, fci.getNamespace(), fci.getName(), inputNames.toArray(new String[0]), finputs, foutputs, false); fcall.setParseInfo(fci); output.add(fcall); } - else if ( source instanceof BuiltinFunctionExpression && ((BuiltinFunctionExpression)source).multipleReturns() ) { + else if(source instanceof BuiltinFunctionExpression && + ((BuiltinFunctionExpression) source).multipleReturns()) { // construct input hops - Hop fcall = processMultipleReturnBuiltinFunctionExpression((BuiltinFunctionExpression)source, mas.getTargetList(), ids); + Hop fcall = processMultipleReturnBuiltinFunctionExpression((BuiltinFunctionExpression) source, + mas.getTargetList(), ids); output.add(fcall); } - else if ( source instanceof ParameterizedBuiltinFunctionExpression && ((ParameterizedBuiltinFunctionExpression)source).multipleReturns() ) { + else if(source instanceof ParameterizedBuiltinFunctionExpression && + ((ParameterizedBuiltinFunctionExpression) source).multipleReturns()) { // construct input hops - Hop fcall = processMultipleReturnParameterizedBuiltinFunctionExpression((ParameterizedBuiltinFunctionExpression)source, mas.getTargetList(), ids); + Hop fcall = processMultipleReturnParameterizedBuiltinFunctionExpression( + (ParameterizedBuiltinFunctionExpression) source, mas.getTargetList(), ids); output.add(fcall); } else - throw new LanguageException("Class \"" + source.getClass() + "\" is not supported in Multiple Assignment statements"); + throw new LanguageException( + "Class \"" + source.getClass() + "\" is not supported in Multiple Assignment statements"); } } @@ -1316,29 +1322,31 @@ else if ( source instanceof ParameterizedBuiltinFunctionExpression && ((Paramete private static DataIdentifier getAccumulatorData(VariableSet liveIn, String varname) { DataIdentifier accum = liveIn.getVariable(varname); - if( accum == null ) - throw new LanguageException("Invalid accumulator assignment " - + "to non-existing variable "+varname+"."); + if(accum == null) + throw new LanguageException( + "Invalid accumulator assignment " + "to non-existing variable " + varname + "."); return accum; } - private void appendDefaultArguments(FunctionStatement fstmt, List inputNames, List inputs, HashMap ids) { - //NOTE: For default expressions of unspecified function arguments, we have two choices: - //either (a) compile ifelse(exist(argName),default, argName) into the function, or - //simply (b) add the default to the argument list of function calls when needed. - //We decided for (b) because it simplifies IPA and dynamic recompilation. + private void appendDefaultArguments(FunctionStatement fstmt, List inputNames, List inputs, + HashMap ids) { + // NOTE: For default expressions of unspecified function arguments, we have two choices: + // either (a) compile ifelse(exist(argName),default, argName) into the function, or + // simply (b) add the default to the argument list of function calls when needed. + // We decided for (b) because it simplifies IPA and dynamic recompilation. - if( fstmt.getInputParams().size() == inputs.size() ) + if(fstmt.getInputParams().size() == inputs.size()) return; HashSet probeNames = new HashSet<>(inputNames); - for( DataIdentifier di : fstmt.getInputParams() ) { - if( probeNames.contains(di.getName()) ) continue; + for(DataIdentifier di : fstmt.getInputParams()) { + if(probeNames.contains(di.getName())) + continue; Expression exp = fstmt.getInputDefault(di.getName()); - if( exp == null ) { - throw new LanguageException("Missing default expression for unspecified " - + "function argument '"+di.getName()+"' in call to function '"+fstmt.getName()+"'."); + if(exp == null) { + throw new LanguageException("Missing default expression for unspecified " + "function argument '" + + di.getName() + "' in call to function '" + fstmt.getName() + "'."); } - //compile and add default expression + // compile and add default expression inputNames.add(di.getName()); inputs.add(processExpression(exp, null, ids)); } @@ -1353,12 +1361,12 @@ public void constructHopsForIfControlBlock(IfStatementBlock sb) { constructHopsForConditionalPredicate(sb); // handle if statement body - for( StatementBlock current : ifBody ) { + for(StatementBlock current : ifBody) { constructHops(current); } // handle else stmt body - for( StatementBlock current : elseBody ) { + for(StatementBlock current : elseBody) { constructHops(current); } } @@ -1368,24 +1376,24 @@ public void constructHopsForIfControlBlock(IfStatementBlock sb) { * * @param sb for statement block */ - public void constructHopsForForControlBlock(ForStatementBlock sb) { + public void constructHopsForForControlBlock(ForStatementBlock sb) { ForStatement fs = (ForStatement) sb.getStatement(0); ArrayList body = fs.getBody(); constructHopsForIterablePredicate(sb); - for( StatementBlock current : body ) + for(StatementBlock current : body) constructHops(current); } public void constructHopsForFunctionControlBlock(FunctionStatementBlock fsb) { - ArrayList body = ((FunctionStatement)fsb.getStatement(0)).getBody(); - for( StatementBlock current : body ) + ArrayList body = ((FunctionStatement) fsb.getStatement(0)).getBody(); + for(StatementBlock current : body) constructHops(current); } public void constructHopsForWhileControlBlock(WhileStatementBlock sb) { - ArrayList body = ((WhileStatement)sb.getStatement(0)).getBody(); + ArrayList body = ((WhileStatement) sb.getStatement(0)).getBody(); constructHopsForConditionalPredicate(sb); - for( StatementBlock current : body ) + for(StatementBlock current : body) constructHops(current); } @@ -1396,12 +1404,12 @@ public void constructHopsForConditionalPredicate(StatementBlock passedSB) { // set conditional predicate ConditionalPredicate cp = null; - if (passedSB instanceof WhileStatementBlock){ - WhileStatement ws = (WhileStatement) ((WhileStatementBlock)passedSB).getStatement(0); + if(passedSB instanceof WhileStatementBlock) { + WhileStatement ws = (WhileStatement) ((WhileStatementBlock) passedSB).getStatement(0); cp = ws.getConditionalPredicate(); - } - else if (passedSB instanceof IfStatementBlock) { - IfStatement ws = (IfStatement) ((IfStatementBlock)passedSB).getStatement(0); + } + else if(passedSB instanceof IfStatementBlock) { + IfStatement ws = (IfStatement) ((IfStatementBlock) passedSB).getStatement(0); cp = ws.getConditionalPredicate(); } else { @@ -1410,21 +1418,24 @@ else if (passedSB instanceof IfStatementBlock) { VariableSet varsRead = cp.variablesRead(); - for (String varName : varsRead.getVariables().keySet()) { + for(String varName : varsRead.getVariables().keySet()) { // creating transient read for live in variables DataIdentifier var = passedSB.liveIn().getVariables().get(varName); DataOp read = null; - if (var == null) { + if(var == null) { throw new ParseException("variable " + varName + " not live variable for conditional predicate"); - } else { - long actualDim1 = (var instanceof IndexedIdentifier) ? ((IndexedIdentifier)var).getOrigDim1() : var.getDim1(); - long actualDim2 = (var instanceof IndexedIdentifier) ? ((IndexedIdentifier)var).getOrigDim2() : var.getDim2(); + } + else { + long actualDim1 = (var instanceof IndexedIdentifier) ? ((IndexedIdentifier) var).getOrigDim1() : var + .getDim1(); + long actualDim2 = (var instanceof IndexedIdentifier) ? ((IndexedIdentifier) var).getOrigDim2() : var + .getDim2(); - read = new DataOp(var.getName(), var.getDataType(), var.getValueType(), OpOpData.TRANSIENTREAD, - null, actualDim1, actualDim2, var.getNnz(), var.getBlocksize()); + read = new DataOp(var.getName(), var.getDataType(), var.getValueType(), OpOpData.TRANSIENTREAD, null, + actualDim1, actualDim2, var.getNnz(), var.getBlocksize()); read.setParseInfo(var); } _ids.put(varName, read); @@ -1437,193 +1448,199 @@ else if (passedSB instanceof IfStatementBlock) { Hop predicateHops = null; Expression predicate = cp.getPredicate(); - if (predicate instanceof RelationalExpression) { + if(predicate instanceof RelationalExpression) { predicateHops = processRelationalExpression((RelationalExpression) cp.getPredicate(), target, _ids); - } else if (predicate instanceof BooleanExpression) { + } + else if(predicate instanceof BooleanExpression) { predicateHops = processBooleanExpression((BooleanExpression) cp.getPredicate(), target, _ids); - } else if (predicate instanceof DataIdentifier) { + } + else if(predicate instanceof DataIdentifier) { // handle data identifier predicate predicateHops = processExpression(cp.getPredicate(), null, _ids); - } else if (predicate instanceof ConstIdentifier) { + } + else if(predicate instanceof ConstIdentifier) { // handle constant identifier - // a) translate 0 --> FALSE; translate 1 --> TRUE - // b) disallow string values - if ((predicate instanceof IntIdentifier && ((IntIdentifier) predicate).getValue() == 0) - || (predicate instanceof DoubleIdentifier && ((DoubleIdentifier) predicate).getValue() == 0.0)) { + // a) translate 0 --> FALSE; translate 1 --> TRUE + // b) disallow string values + if((predicate instanceof IntIdentifier && ((IntIdentifier) predicate).getValue() == 0) || + (predicate instanceof DoubleIdentifier && ((DoubleIdentifier) predicate).getValue() == 0.0)) { cp.setPredicate(new BooleanIdentifier(false, predicate)); - } else if ((predicate instanceof IntIdentifier && ((IntIdentifier) predicate).getValue() == 1) - || (predicate instanceof DoubleIdentifier && ((DoubleIdentifier) predicate).getValue() == 1.0)) { + } + else if((predicate instanceof IntIdentifier && ((IntIdentifier) predicate).getValue() == 1) || + (predicate instanceof DoubleIdentifier && ((DoubleIdentifier) predicate).getValue() == 1.0)) { cp.setPredicate(new BooleanIdentifier(true, predicate)); - } else if (predicate instanceof IntIdentifier || predicate instanceof DoubleIdentifier) { + } + else if(predicate instanceof IntIdentifier || predicate instanceof DoubleIdentifier) { cp.setPredicate(new BooleanIdentifier(true, predicate)); LOG.warn(predicate.printWarningLocation() + "Numerical value '" + predicate.toString() - + "' (!= 0/1) is converted to boolean TRUE by DML"); - } else if (predicate instanceof StringIdentifier) { + + "' (!= 0/1) is converted to boolean TRUE by DML"); + } + else if(predicate instanceof StringIdentifier) { throw new ParseException(predicate.printErrorLocation() + "String value '" + predicate.toString() - + "' is not allowed for iterable predicate"); + + "' is not allowed for iterable predicate"); } predicateHops = processExpression(cp.getPredicate(), null, _ids); } - //create transient write to internal variable name on top of expression - //in order to ensure proper instruction generation - predicateHops = HopRewriteUtils.createDataOp( - ProgramBlock.PRED_VAR, predicateHops, OpOpData.TRANSIENTWRITE); + // create transient write to internal variable name on top of expression + // in order to ensure proper instruction generation + predicateHops = HopRewriteUtils.createDataOp(ProgramBlock.PRED_VAR, predicateHops, OpOpData.TRANSIENTWRITE); - if (passedSB instanceof WhileStatementBlock) - ((WhileStatementBlock)passedSB).setPredicateHops(predicateHops); - else if (passedSB instanceof IfStatementBlock) - ((IfStatementBlock)passedSB).setPredicateHops(predicateHops); + if(passedSB instanceof WhileStatementBlock) + ((WhileStatementBlock) passedSB).setPredicateHops(predicateHops); + else if(passedSB instanceof IfStatementBlock) + ((IfStatementBlock) passedSB).setPredicateHops(predicateHops); } /** - * Constructs all predicate Hops (for FROM, TO, INCREMENT) of an iterable predicate - * and assigns these Hops to the passed statement block. + * Constructs all predicate Hops (for FROM, TO, INCREMENT) of an iterable predicate and assigns these Hops to the + * passed statement block. * * Method used for both ForStatementBlock and ParForStatementBlock. * * @param fsb for statement block */ - public void constructHopsForIterablePredicate(ForStatementBlock fsb) - { + public void constructHopsForIterablePredicate(ForStatementBlock fsb) { HashMap _ids = new HashMap<>(); - // set iterable predicate + // set iterable predicate ForStatement fs = (ForStatement) fsb.getStatement(0); IterablePredicate ip = fs.getIterablePredicate(); - for(int i=0; i < 3; i++) { - Expression expr = (i == 0) ? ip.getFromExpr() : (i == 1) ? ip.getToExpr() : - ( ip.getIncrementExpr() != null ) ? ip.getIncrementExpr() : null; + for(int i = 0; i < 3; i++) { + Expression expr = (i == 0) ? ip.getFromExpr() : (i == 1) ? ip + .getToExpr() : (ip.getIncrementExpr() != null) ? ip.getIncrementExpr() : null; VariableSet varsRead = (expr != null) ? expr.variablesRead() : null; if(varsRead != null) { - for (String varName : varsRead.getVariables().keySet()) { + for(String varName : varsRead.getVariables().keySet()) { DataIdentifier var = fsb.liveIn().getVariable(varName); DataOp read = null; - if (var == null) { + if(var == null) { throw new ParseException("variable '" + varName + "' is not available for iterable predicate"); } else { - long actualDim1 = (var instanceof IndexedIdentifier) ? ((IndexedIdentifier)var).getOrigDim1() : var.getDim1(); - long actualDim2 = (var instanceof IndexedIdentifier) ? ((IndexedIdentifier)var).getOrigDim2() : var.getDim2(); + long actualDim1 = (var instanceof IndexedIdentifier) ? ((IndexedIdentifier) var) + .getOrigDim1() : var.getDim1(); + long actualDim2 = (var instanceof IndexedIdentifier) ? ((IndexedIdentifier) var) + .getOrigDim2() : var.getDim2(); read = new DataOp(var.getName(), var.getDataType(), var.getValueType(), OpOpData.TRANSIENTREAD, - null, actualDim1, actualDim2, var.getNnz(), var.getBlocksize()); + null, actualDim1, actualDim2, var.getNnz(), var.getBlocksize()); read.setParseInfo(var); } _ids.put(varName, read); } } - //create transient write to internal variable name on top of expression - //in order to ensure proper instruction generation + // create transient write to internal variable name on top of expression + // in order to ensure proper instruction generation Hop predicateHops = processTempIntExpression(expr, _ids); - if( predicateHops != null ) - predicateHops = HopRewriteUtils.createDataOp( - ProgramBlock.PRED_VAR, predicateHops, OpOpData.TRANSIENTWRITE); - - //construct hops for from, to, and increment expressions - if( i == 0 ) - fsb.setFromHops( predicateHops ); - else if( i == 1 ) - fsb.setToHops( predicateHops ); - else if( ip.getIncrementExpr() != null ) - fsb.setIncrementHops( predicateHops ); + if(predicateHops != null) + predicateHops = HopRewriteUtils.createDataOp(ProgramBlock.PRED_VAR, predicateHops, + OpOpData.TRANSIENTWRITE); + + // construct hops for from, to, and increment expressions + if(i == 0) + fsb.setFromHops(predicateHops); + else if(i == 1) + fsb.setToHops(predicateHops); + else if(ip.getIncrementExpr() != null) + fsb.setIncrementHops(predicateHops); } } /** - * Construct Hops from parse tree : Process Expression in an assignment - * statement + * Construct Hops from parse tree : Process Expression in an assignment statement * * @param source source expression * @param target data identifier - * @param hops map of high-level operators + * @param hops map of high-level operators * @return high-level operator */ private Hop processExpression(Expression source, DataIdentifier target, HashMap hops) { try { - if( source instanceof BinaryExpression ) + if(source instanceof BinaryExpression) return processBinaryExpression((BinaryExpression) source, target, hops); - else if( source instanceof RelationalExpression ) + else if(source instanceof RelationalExpression) return processRelationalExpression((RelationalExpression) source, target, hops); - else if( source instanceof BooleanExpression ) + else if(source instanceof BooleanExpression) return processBooleanExpression((BooleanExpression) source, target, hops); - else if( source instanceof BuiltinFunctionExpression ) + else if(source instanceof BuiltinFunctionExpression) return processBuiltinFunctionExpression((BuiltinFunctionExpression) source, target, hops); - else if( source instanceof ParameterizedBuiltinFunctionExpression ) - return processParameterizedBuiltinFunctionExpression((ParameterizedBuiltinFunctionExpression)source, target, hops); - else if( source instanceof DataExpression ) { - Hop ae = processDataExpression((DataExpression)source, target, hops); - if (ae instanceof DataOp && ((DataOp) ae).getOp() != OpOpData.SQLREAD && - ((DataOp) ae).getOp() != OpOpData.FEDERATED) { - Expression expr = ((DataExpression)source).getVarParam(DataExpression.FORMAT_TYPE); - if( expr instanceof StringIdentifier ) - ((DataOp)ae).setFileFormat(Expression.convertFormatType(expr.toString())); + else if(source instanceof ParameterizedBuiltinFunctionExpression) + return processParameterizedBuiltinFunctionExpression((ParameterizedBuiltinFunctionExpression) source, + target, hops); + else if(source instanceof DataExpression) { + Hop ae = processDataExpression((DataExpression) source, target, hops); + if(ae instanceof DataOp && ((DataOp) ae).getOp() != OpOpData.SQLREAD && + ((DataOp) ae).getOp() != OpOpData.FEDERATED) { + Expression expr = ((DataExpression) source).getVarParam(DataExpression.FORMAT_TYPE); + if(expr instanceof StringIdentifier) + ((DataOp) ae).setFileFormat(Expression.convertFormatType(expr.toString())); else - ((DataOp)ae).setFileFormat(FileFormat.UNKNOWN); + ((DataOp) ae).setFileFormat(FileFormat.UNKNOWN); } return ae; } - else if (source instanceof IndexedIdentifier) - return processIndexingExpression((IndexedIdentifier) source,target,hops); - else if (source instanceof IntIdentifier) { + else if(source instanceof IndexedIdentifier) + return processIndexingExpression((IndexedIdentifier) source, target, hops); + else if(source instanceof IntIdentifier) { IntIdentifier sourceInt = (IntIdentifier) source; LiteralOp litop = new LiteralOp(sourceInt.getValue()); litop.setParseInfo(sourceInt); setIdentifierParams(litop, sourceInt); return litop; - } - else if (source instanceof DoubleIdentifier) { + } + else if(source instanceof DoubleIdentifier) { DoubleIdentifier sourceDouble = (DoubleIdentifier) source; LiteralOp litop = new LiteralOp(sourceDouble.getValue()); litop.setParseInfo(sourceDouble); setIdentifierParams(litop, sourceDouble); return litop; } - else if (source instanceof BooleanIdentifier) { + else if(source instanceof BooleanIdentifier) { BooleanIdentifier sourceBoolean = (BooleanIdentifier) source; LiteralOp litop = new LiteralOp(sourceBoolean.getValue()); litop.setParseInfo(sourceBoolean); setIdentifierParams(litop, sourceBoolean); return litop; - } - else if (source instanceof StringIdentifier) { + } + else if(source instanceof StringIdentifier) { StringIdentifier sourceString = (StringIdentifier) source; LiteralOp litop = new LiteralOp(sourceString.getValue()); litop.setParseInfo(sourceString); setIdentifierParams(litop, sourceString); return litop; - } - else if (source instanceof DataIdentifier) + } + else if(source instanceof DataIdentifier) return hops.get(((DataIdentifier) source).getName()); - else if (source instanceof ExpressionList){ + else if(source instanceof ExpressionList) { ExpressionList sourceList = (ExpressionList) source; List expressions = sourceList.getValue(); Hop[] listHops = new Hop[expressions.size()]; int idx = 0; - for( Expression ex : expressions){ + for(Expression ex : expressions) { listHops[idx++] = processExpression(ex, null, hops); } - Hop currBuiltinOp = HopRewriteUtils.createNary(OpOpN.LIST,listHops ); + Hop currBuiltinOp = HopRewriteUtils.createNary(OpOpN.LIST, listHops); return currBuiltinOp; } - else{ + else { throw new ParseException("Unhandled instance of source type: " + source.getClass()); } - } - catch(ParseException e ){ + } + catch(ParseException e) { throw e; } - catch ( Exception e ) { + catch(Exception e) { throw new ParseException("A Parsing exception occurred", e); } } private static DataIdentifier createTarget(Expression source) { Identifier id = source.getOutput(); - if (id instanceof DataIdentifier && !(id instanceof DataExpression)) + if(id instanceof DataIdentifier && !(id instanceof DataExpression)) return (DataIdentifier) id; DataIdentifier target = new DataIdentifier(Expression.getTempName()); target.setProperties(id); @@ -1635,20 +1652,20 @@ private static DataIdentifier createTarget() { } /** - * Constructs the Hops for arbitrary expressions that eventually evaluate to an INT scalar. + * Constructs the Hops for arbitrary expressions that eventually evaluate to an INT scalar. * * @param source source expression - * @param hops map of high-level operators + * @param hops map of high-level operators * @return high-level operatos */ - private Hop processTempIntExpression( Expression source, HashMap hops ) { - if( source == null ) + private Hop processTempIntExpression(Expression source, HashMap hops) { + if(source == null) return null; DataIdentifier tmpOut = createTarget(); tmpOut.setDataType(DataType.SCALAR); tmpOut.setValueType(ValueType.INT64); source.setOutput(tmpOut); - return processExpression(source, tmpOut, hops ); + return processExpression(source, tmpOut, hops); } private Hop processLeftIndexedExpression(Expression source, IndexedIdentifier target, HashMap hops) { @@ -1660,16 +1677,17 @@ private Hop processLeftIndexedExpression(Expression source, IndexedIdentifier ta // process the target to get targetHops Hop targetOp = hops.get(target.getName()); - if (targetOp == null){ - throw new ParseException(target.printErrorLocation() + " must define matrix " + target.getName() + " before indexing operations are allowed "); + if(targetOp == null) { + throw new ParseException(target.printErrorLocation() + " must define matrix " + target.getName() + + " before indexing operations are allowed "); } - if( sourceOp.getDataType().isMatrix() && source.getOutput().getDataType().isScalar() ) + if(sourceOp.getDataType().isMatrix() && source.getOutput().getDataType().isScalar()) sourceOp.setDataType(DataType.SCALAR); - Hop leftIndexOp = new LeftIndexingOp(target.getName(), target.getDataType(), - ValueType.FP64, targetOp, sourceOp, ixRange[0], ixRange[1], ixRange[2], ixRange[3], - target.getRowLowerEqualsUpper(), target.getColLowerEqualsUpper()); + Hop leftIndexOp = new LeftIndexingOp(target.getName(), target.getDataType(), ValueType.FP64, targetOp, sourceOp, + ixRange[0], ixRange[1], ixRange[2], ixRange[3], target.getRowLowerEqualsUpper(), + target.getColLowerEqualsUpper()); setIdentifierParams(leftIndexOp, target); leftIndexOp.setParseInfo(target); @@ -1683,16 +1701,16 @@ private Hop processIndexingExpression(IndexedIdentifier source, DataIdentifier t // process Hops for indexes (for source) Hop[] ixRange = getIndexingBounds(source, hops, false); - if (target == null) { + if(target == null) { target = createTarget(source); } - //unknown nnz after range indexing (applies to indexing op but also - //data dependent operations) - target.setNnz(-1); + // unknown nnz after range indexing (applies to indexing op but also + // data dependent operations) + target.setNnz(-1); Hop indexOp = new IndexingOp(target.getName(), target.getDataType(), target.getValueType(), - hops.get(source.getName()), ixRange[0], ixRange[1], ixRange[2], ixRange[3], - source.getRowLowerEqualsUpper(), source.getColLowerEqualsUpper()); + hops.get(source.getName()), ixRange[0], ixRange[1], ixRange[2], ixRange[3], source.getRowLowerEqualsUpper(), + source.getColLowerEqualsUpper()); indexOp.setParseInfo(target); setIdentifierParams(indexOp, target); @@ -1701,27 +1719,27 @@ private Hop processIndexingExpression(IndexedIdentifier source, DataIdentifier t } private Hop[] getIndexingBounds(IndexedIdentifier ix, HashMap hops, boolean lix) { - Hop rowLowerHops = (ix.getRowLowerBound() != null) ? - processExpression(ix.getRowLowerBound(),null, hops) : new LiteralOp(1); - Hop colLowerHops = (ix.getColLowerBound() != null) ? - processExpression(ix.getColLowerBound(),null, hops) : new LiteralOp(1); + Hop rowLowerHops = (ix.getRowLowerBound() != null) ? processExpression(ix.getRowLowerBound(), null, + hops) : new LiteralOp(1); + Hop colLowerHops = (ix.getColLowerBound() != null) ? processExpression(ix.getColLowerBound(), null, + hops) : new LiteralOp(1); Hop rowUpperHops = null, colUpperHops = null; - if (ix.getRowUpperBound() != null) - rowUpperHops = processExpression(ix.getRowUpperBound(),null,hops); + if(ix.getRowUpperBound() != null) + rowUpperHops = processExpression(ix.getRowUpperBound(), null, hops); else { - rowUpperHops = ((lix ? ix.getDim1() : ix.getOrigDim1()) != -1) ? - new LiteralOp(ix.getOrigDim1()) : - new UnaryOp(ix.getName(), DataType.SCALAR, ValueType.INT64, OpOp1.NROW, hops.get(ix.getName())); + rowUpperHops = ((lix ? ix.getDim1() : ix.getOrigDim1()) != -1) ? new LiteralOp( + ix.getOrigDim1()) : new UnaryOp(ix.getName(), DataType.SCALAR, ValueType.INT64, OpOp1.NROW, + hops.get(ix.getName())); rowUpperHops.setParseInfo(ix); } - if (ix.getColUpperBound() != null) - colUpperHops = processExpression(ix.getColUpperBound(),null,hops); + if(ix.getColUpperBound() != null) + colUpperHops = processExpression(ix.getColUpperBound(), null, hops); else { - colUpperHops = ((lix ? ix.getDim2() : ix.getOrigDim2()) != -1) ? - new LiteralOp(ix.getOrigDim2()) : - new UnaryOp(ix.getName(), DataType.SCALAR, ValueType.INT64, OpOp1.NCOL, hops.get(ix.getName())); + colUpperHops = ((lix ? ix.getDim2() : ix.getOrigDim2()) != -1) ? new LiteralOp( + ix.getOrigDim2()) : new UnaryOp(ix.getName(), DataType.SCALAR, ValueType.INT64, OpOp1.NCOL, + hops.get(ix.getName())); colUpperHops.setParseInfo(ix); } @@ -1729,33 +1747,31 @@ private Hop[] getIndexingBounds(IndexedIdentifier ix, HashMap hops, } /** - * Construct Hops from parse tree : Process Binary Expression in an - * assignment statement + * Construct Hops from parse tree : Process Binary Expression in an assignment statement * * @param source binary expression * @param target data identifier - * @param hops map of high-level operators + * @param hops map of high-level operators * @return high-level operator */ - private Hop processBinaryExpression(BinaryExpression source, DataIdentifier target, HashMap hops) - { - Hop left = processExpression(source.getLeft(), null, hops); + private Hop processBinaryExpression(BinaryExpression source, DataIdentifier target, HashMap hops) { + Hop left = processExpression(source.getLeft(), null, hops); Hop right = processExpression(source.getRight(), null, hops); - if (left == null || right == null) { - throw new ParseException("Missing input in binary expressions (" + source.toString()+"): " - + ((left==null)?source.getLeft():source.getRight())+", line="+source.getBeginLine()); + if(left == null || right == null) { + throw new ParseException("Missing input in binary expressions (" + source.toString() + "): " + + ((left == null) ? source.getLeft() : source.getRight()) + ", line=" + source.getBeginLine()); } - //prepare target identifier and ensure that output type is of inferred type - //(type should not be determined by target (e.g., string for print) - if (target == null) { + // prepare target identifier and ensure that output type is of inferred type + // (type should not be determined by target (e.g., string for print) + if(target == null) { target = createTarget(source); } target.setValueType(source.getOutput().getValueType()); Hop currBop = null; - switch( source.getOpCode() ) { + switch(source.getOpCode()) { case PLUS: case MINUS: case MULT: @@ -1763,14 +1779,15 @@ private Hop processBinaryExpression(BinaryExpression source, DataIdentifier targ case MODULUS: case POW: case INTDIV: - currBop = new BinaryOp(target.getName(), target.getDataType(), - target.getValueType(), OpOp2.valueOf(source.getOpCode().name()), left, right); + currBop = new BinaryOp(target.getName(), target.getDataType(), target.getValueType(), + OpOp2.valueOf(source.getOpCode().name()), left, right); break; case MATMULT: - currBop = new AggBinaryOp(target.getName(), target.getDataType(), target.getValueType(), OpOp2.MULT, org.apache.sysds.common.Types.AggOp.SUM, left, right); + currBop = new AggBinaryOp(target.getName(), target.getDataType(), target.getValueType(), OpOp2.MULT, + org.apache.sysds.common.Types.AggOp.SUM, left, right); break; default: - throw new ParseException("Unsupported parsing of binary expression: "+source.getOpCode()); + throw new ParseException("Unsupported parsing of binary expression: " + source.getOpCode()); } setIdentifierParams(currBop, source.getOutput()); @@ -1778,14 +1795,15 @@ private Hop processBinaryExpression(BinaryExpression source, DataIdentifier targ return currBop; } - private Hop processRelationalExpression(RelationalExpression source, DataIdentifier target, HashMap hops) { + private Hop processRelationalExpression(RelationalExpression source, DataIdentifier target, + HashMap hops) { Hop left = processExpression(source.getLeft(), null, hops); Hop right = processExpression(source.getRight(), null, hops); Hop currBop = null; - if (target == null) { + if(target == null) { target = createTarget(source); if(left.getDataType() == DataType.MATRIX || right.getDataType() == DataType.MATRIX) { // Added to support matrix relational comparison @@ -1806,17 +1824,22 @@ else if(left.getDataType() == DataType.FRAME || right.getDataType() == DataType. OpOp2 op = null; - if (source.getOpCode() == Expression.RelationalOp.LESS) { + if(source.getOpCode() == Expression.RelationalOp.LESS) { op = OpOp2.LESS; - } else if (source.getOpCode() == Expression.RelationalOp.LESSEQUAL) { + } + else if(source.getOpCode() == Expression.RelationalOp.LESSEQUAL) { op = OpOp2.LESSEQUAL; - } else if (source.getOpCode() == Expression.RelationalOp.GREATER) { + } + else if(source.getOpCode() == Expression.RelationalOp.GREATER) { op = OpOp2.GREATER; - } else if (source.getOpCode() == Expression.RelationalOp.GREATEREQUAL) { + } + else if(source.getOpCode() == Expression.RelationalOp.GREATEREQUAL) { op = OpOp2.GREATEREQUAL; - } else if (source.getOpCode() == Expression.RelationalOp.EQUAL) { + } + else if(source.getOpCode() == Expression.RelationalOp.EQUAL) { op = OpOp2.EQUAL; - } else if (source.getOpCode() == Expression.RelationalOp.NOTEQUAL) { + } + else if(source.getOpCode() == Expression.RelationalOp.NOTEQUAL) { op = OpOp2.NOTEQUAL; } currBop = new BinaryOp(target.getName(), target.getDataType(), target.getValueType(), op, left, right); @@ -1824,47 +1847,49 @@ else if(left.getDataType() == DataType.FRAME || right.getDataType() == DataType. return currBop; } - private Hop processBooleanExpression(BooleanExpression source, DataIdentifier target, HashMap hops) - { + private Hop processBooleanExpression(BooleanExpression source, DataIdentifier target, HashMap hops) { // Boolean Not has a single parameter boolean constLeft = (source.getLeft().getOutput() instanceof ConstIdentifier); boolean constRight = false; - if (source.getRight() != null) { + if(source.getRight() != null) { constRight = (source.getRight().getOutput() instanceof ConstIdentifier); } - if (constLeft || constRight) { + if(constLeft || constRight) { throw new RuntimeException(source.printErrorLocation() + "Boolean expression with constant unsupported"); } Hop left = processExpression(source.getLeft(), null, hops); Hop right = null; - if (source.getRight() != null) { + if(source.getRight() != null) { right = processExpression(source.getRight(), null, hops); } - //prepare target identifier and ensure that output type is boolean - //(type should not be determined by target (e.g., string for print) - if (target == null) + // prepare target identifier and ensure that output type is boolean + // (type should not be determined by target (e.g., string for print) + if(target == null) target = createTarget(source); - if( target.getDataType().isScalar() ) + if(target.getDataType().isScalar()) target.setValueType(ValueType.BOOLEAN); - if (source.getRight() == null) { + if(source.getRight() == null) { Hop currUop = new UnaryOp(target.getName(), target.getDataType(), target.getValueType(), OpOp1.NOT, left); currUop.setParseInfo(source); return currUop; - } + } else { Hop currBop = null; OpOp2 op = null; - if (source.getOpCode() == Expression.BooleanOp.LOGICALAND) { + if(source.getOpCode() == Expression.BooleanOp.LOGICALAND) { op = OpOp2.AND; - } else if (source.getOpCode() == Expression.BooleanOp.LOGICALOR) { + } + else if(source.getOpCode() == Expression.BooleanOp.LOGICALOR) { op = OpOp2.OR; - } else { - throw new RuntimeException(source.printErrorLocation() + "Unknown boolean operation " + source.getOpCode()); + } + else { + throw new RuntimeException( + source.printErrorLocation() + "Unknown boolean operation " + source.getOpCode()); } currBop = new BinaryOp(target.getName(), target.getDataType(), target.getValueType(), op, left, right); currBop.setParseInfo(source); @@ -1873,49 +1898,51 @@ private Hop processBooleanExpression(BooleanExpression source, DataIdentifier ta } } - private static Hop constructDfHop(String name, DataType dt, ValueType vt, Builtins op, LinkedHashMap paramHops) { + private static Hop constructDfHop(String name, DataType dt, ValueType vt, Builtins op, + LinkedHashMap paramHops) { - // Add a hop to paramHops to store distribution information. + // Add a hop to paramHops to store distribution information. // Distribution parameter hops would have been already present in paramHops. Hop distLop = null; switch(op) { - case QNORM: - case PNORM: - distLop = new LiteralOp("normal"); - break; - case QT: - case PT: - distLop = new LiteralOp("t"); - break; - case QF: - case PF: - distLop = new LiteralOp("f"); - break; - case QCHISQ: - case PCHISQ: - distLop = new LiteralOp("chisq"); - break; - case QEXP: - case PEXP: - distLop = new LiteralOp("exp"); - break; - - case CDF: - case INVCDF: - break; - - default: - throw new HopsException("Invalid operation: " + op); - } - if (distLop != null) + case QNORM: + case PNORM: + distLop = new LiteralOp("normal"); + break; + case QT: + case PT: + distLop = new LiteralOp("t"); + break; + case QF: + case PF: + distLop = new LiteralOp("f"); + break; + case QCHISQ: + case PCHISQ: + distLop = new LiteralOp("chisq"); + break; + case QEXP: + case PEXP: + distLop = new LiteralOp("exp"); + break; + + case CDF: + case INVCDF: + break; + + default: + throw new HopsException("Invalid operation: " + op); + } + if(distLop != null) paramHops.put("dist", distLop); - return new ParameterizedBuiltinOp(name, dt, vt, ParameterizedBuiltinFunctionExpression.pbHopMap.get(op), paramHops); + return new ParameterizedBuiltinOp(name, dt, vt, ParameterizedBuiltinFunctionExpression.pbHopMap.get(op), + paramHops); } - private Hop processMultipleReturnParameterizedBuiltinFunctionExpression(ParameterizedBuiltinFunctionExpression source, ArrayList targetList, - HashMap hops) - { + private Hop processMultipleReturnParameterizedBuiltinFunctionExpression( + ParameterizedBuiltinFunctionExpression source, ArrayList targetList, + HashMap hops) { FunctionType ftype = FunctionType.MULTIRETURN_BUILTIN; String nameSpace = DMLProgram.INTERNAL_NAMESPACE; @@ -1925,27 +1952,32 @@ private Hop processMultipleReturnParameterizedBuiltinFunctionExpression(Paramete // Construct Hop for current builtin function expression based on its type Hop currBuiltinOp = null; - switch (source.getOpCode()) { + switch(source.getOpCode()) { case TRANSFORMENCODE: ArrayList inputs = new ArrayList<>(); - inputs.add( processExpression(source.getVarParam("target"), null, hops) ); - inputs.add( processExpression(source.getVarParam("spec"), null, hops) ); - String[] outputNames = new String[targetList.size()]; + inputs.add(processExpression(source.getVarParam("target"), null, hops)); + inputs.add(processExpression(source.getVarParam("spec"), null, hops)); + String[] outputNames = new String[targetList.size()]; outputNames[0] = targetList.get(0).getName(); outputNames[1] = targetList.get(1).getName(); - outputs.add(new DataOp(outputNames[0], DataType.MATRIX, ValueType.FP64, inputs.get(0), OpOpData.FUNCTIONOUTPUT, inputs.get(0).getFilename())); - outputs.add(new DataOp(outputNames[1], DataType.FRAME, ValueType.STRING, inputs.get(0), OpOpData.FUNCTIONOUTPUT, inputs.get(0).getFilename())); + outputs.add(new DataOp(outputNames[0], DataType.MATRIX, ValueType.FP64, inputs.get(0), + OpOpData.FUNCTIONOUTPUT, inputs.get(0).getFilename())); + outputs.add(new DataOp(outputNames[1], DataType.FRAME, ValueType.STRING, inputs.get(0), + OpOpData.FUNCTIONOUTPUT, inputs.get(0).getFilename())); - currBuiltinOp = new FunctionOp(ftype, nameSpace, source.getOpCode().toString(), null, inputs, outputNames, outputs); + currBuiltinOp = new FunctionOp(ftype, nameSpace, source.getOpCode().toString(), null, inputs, + outputNames, outputs); break; default: - throw new ParseException("Invaid Opcode in DMLTranslator:processMultipleReturnParameterizedBuiltinFunctionExpression(): " + source.getOpCode()); + throw new ParseException( + "Invaid Opcode in DMLTranslator:processMultipleReturnParameterizedBuiltinFunctionExpression(): " + + source.getOpCode()); } // set properties for created hops based on outputs of source expression - for ( int i=0; i < source.getOutputs().length; i++ ) { - setIdentifierParams( outputs.get(i), source.getOutputs()[i]); + for(int i = 0; i < source.getOutputs().length; i++) { + setIdentifierParams(outputs.get(i), source.getOutputs()[i]); outputs.get(i).setParseInfo(source); } currBuiltinOp.setParseInfo(source); @@ -1954,16 +1986,15 @@ private Hop processMultipleReturnParameterizedBuiltinFunctionExpression(Paramete } /** - * Construct Hops from parse tree : Process ParameterizedBuiltinFunction Expression in an - * assignment statement + * Construct Hops from parse tree : Process ParameterizedBuiltinFunction Expression in an assignment statement * * @param source parameterized built-in function * @param target data identifier - * @param hops map of high-level operators + * @param hops map of high-level operators * @return high-level operator */ - private Hop processParameterizedBuiltinFunctionExpression(ParameterizedBuiltinFunctionExpression source, DataIdentifier target, - HashMap hops) { + private Hop processParameterizedBuiltinFunctionExpression(ParameterizedBuiltinFunctionExpression source, + DataIdentifier target, HashMap hops) { // this expression has multiple "named" parameters LinkedHashMap paramHops = new LinkedHashMap<>(); @@ -1971,14 +2002,14 @@ private Hop processParameterizedBuiltinFunctionExpression(ParameterizedBuiltinFu // -- construct hops for all input parameters // -- store them in hashmap so that their "name"s are maintained Hop pHop = null; - for ( String paramName : source.getVarParams().keySet() ) { + for(String paramName : source.getVarParams().keySet()) { pHop = processExpression(source.getVarParam(paramName), null, hops); paramHops.put(paramName, pHop); } Hop currBuiltinOp = null; - if (target == null) { + if(target == null) { target = createTarget(source); } @@ -1996,8 +2027,8 @@ private Hop processParameterizedBuiltinFunctionExpression(ParameterizedBuiltinFu case PF: case PCHISQ: case PEXP: - currBuiltinOp = constructDfHop(target.getName(), target.getDataType(), - target.getValueType(), source.getOpCode(), paramHops); + currBuiltinOp = constructDfHop(target.getName(), target.getDataType(), target.getValueType(), + source.getOpCode(), paramHops); break; case CONTAINS: case GROUPEDAGG: @@ -2022,16 +2053,16 @@ private Hop processParameterizedBuiltinFunctionExpression(ParameterizedBuiltinFu inputs.add(paramHops.get("by")); inputs.add(paramHops.get("decreasing")); inputs.add(paramHops.get("index.return")); - currBuiltinOp = new ReorgOp(target.getName(), target.getDataType(), target.getValueType(), ReOrgOp.SORT, inputs); + currBuiltinOp = new ReorgOp(target.getName(), target.getDataType(), target.getValueType(), ReOrgOp.SORT, + inputs); break; case TOSTRING: - //check for input data type and only compile toString Hop for matrices/frames, - //for scalars, we compile (s + "") to ensure consistent string output value types - currBuiltinOp = !paramHops.get("target").getDataType().isScalar() ? - new ParameterizedBuiltinOp(target.getName(), target.getDataType(), - target.getValueType(), ParamBuiltinOp.TOSTRING, paramHops) : - HopRewriteUtils.createBinary(paramHops.get("target"), new LiteralOp(""), OpOp2.PLUS); + // check for input data type and only compile toString Hop for matrices/frames, + // for scalars, we compile (s + "") to ensure consistent string output value types + currBuiltinOp = !paramHops.get("target").getDataType().isScalar() ? new ParameterizedBuiltinOp( + target.getName(), target.getDataType(), target.getValueType(), ParamBuiltinOp.TOSTRING, + paramHops) : HopRewriteUtils.createBinary(paramHops.get("target"), new LiteralOp(""), OpOp2.PLUS); break; case LISTNV: @@ -2041,37 +2072,39 @@ private Hop processParameterizedBuiltinFunctionExpression(ParameterizedBuiltinFu case COUNT_DISTINCT: case COUNT_DISTINCT_APPROX: { - Direction dir = Direction.RowCol; // Default direction - DataType dataType = DataType.SCALAR; // Default output data type + Direction dir = Direction.RowCol; // Default direction + DataType dataType = DataType.SCALAR; // Default output data type LiteralOp dirOp = (LiteralOp) paramHops.get("dir"); - if (dirOp != null) { + if(dirOp != null) { String dirString = dirOp.getStringValue().toUpperCase(); - if (dirString.equals(Direction.RowCol.toString())) { + if(dirString.equals(Direction.RowCol.toString())) { dir = Direction.RowCol; dataType = DataType.SCALAR; - } else if (dirString.equals(Direction.Row.toString())) { + } + else if(dirString.equals(Direction.Row.toString())) { dir = Direction.Row; dataType = DataType.MATRIX; - } else if (dirString.equals(Direction.Col.toString())) { + } + else if(dirString.equals(Direction.Col.toString())) { dir = Direction.Col; dataType = DataType.MATRIX; } } currBuiltinOp = new AggUnaryOp(target.getName(), dataType, target.getValueType(), - AggOp.valueOf(source.getOpCode().name()), dir, paramHops.get("data")); + AggOp.valueOf(source.getOpCode().name()), dir, paramHops.get("data")); break; } case COUNT_DISTINCT_APPROX_ROW: currBuiltinOp = new AggUnaryOp(target.getName(), DataType.MATRIX, target.getValueType(), - AggOp.valueOf(source.getOpCode().name()), Direction.Row, paramHops.get("data")); + AggOp.valueOf(source.getOpCode().name()), Direction.Row, paramHops.get("data")); break; case COUNT_DISTINCT_APPROX_COL: currBuiltinOp = new AggUnaryOp(target.getName(), DataType.MATRIX, target.getValueType(), - AggOp.valueOf(source.getOpCode().name()), Direction.Col, paramHops.get("data")); + AggOp.valueOf(source.getOpCode().name()), Direction.Col, paramHops.get("data")); break; case UNIQUE: @@ -2079,24 +2112,26 @@ private Hop processParameterizedBuiltinFunctionExpression(ParameterizedBuiltinFu DataType dataType = DataType.MATRIX; LiteralOp dirOp = (LiteralOp) paramHops.get("dir"); - if (dirOp != null) { + if(dirOp != null) { String dirString = dirOp.getStringValue().toUpperCase(); - if (dirString.equals(Direction.RowCol.toString())) { + if(dirString.equals(Direction.RowCol.toString())) { dir = Direction.RowCol; - } else if (dirString.equals(Direction.Row.toString())) { + } + else if(dirString.equals(Direction.Row.toString())) { dir = Direction.Row; - } else if (dirString.equals(Direction.Col.toString())) { + } + else if(dirString.equals(Direction.Col.toString())) { dir = Direction.Col; } } currBuiltinOp = new AggUnaryOp(target.getName(), dataType, target.getValueType(), - AggOp.valueOf(source.getOpCode().name()), dir, paramHops.get("data")); + AggOp.valueOf(source.getOpCode().name()), dir, paramHops.get("data")); break; default: - throw new ParseException(source.printErrorLocation() + - "processParameterizedBuiltinFunctionExpression() -- Unknown operation: " + source.getOpCode()); + throw new ParseException(source.printErrorLocation() + + "processParameterizedBuiltinFunctionExpression() -- Unknown operation: " + source.getOpCode()); } setIdentifierParams(currBuiltinOp, source.getOutput()); @@ -2105,96 +2140,96 @@ private Hop processParameterizedBuiltinFunctionExpression(ParameterizedBuiltinFu } /** - * Construct Hops from parse tree : Process ParameterizedExpression in a - * read/write/rand statement + * Construct Hops from parse tree : Process ParameterizedExpression in a read/write/rand statement * * @param source data expression * @param target data identifier - * @param hops map of high-level operators + * @param hops map of high-level operators * @return high-level operator */ - private Hop processDataExpression(DataExpression source, DataIdentifier target, - HashMap hops) { + private Hop processDataExpression(DataExpression source, DataIdentifier target, HashMap hops) { // this expression has multiple "named" parameters HashMap paramHops = new HashMap<>(); // -- construct hops for all input parameters // -- store them in hashmap so that their "name"s are maintained - Hop pHop = null; - for ( String paramName : source.getVarParams().keySet() ) { + Hop pHop = null; + for(String paramName : source.getVarParams().keySet()) { pHop = processExpression(source.getVarParam(paramName), null, hops); paramHops.put(paramName, pHop); } Hop currBuiltinOp = null; - if (target == null) { + if(target == null) { target = createTarget(source); } // construct hop based on opcode switch(source.getOpCode()) { - case READ: - currBuiltinOp = new DataOp(target.getName(), target.getDataType(), target.getValueType(), OpOpData.PERSISTENTREAD, paramHops); - ((DataOp)currBuiltinOp).setFileName(((StringIdentifier)source.getVarParam(DataExpression.IO_FILENAME)).getValue()); - break; - - case WRITE: - currBuiltinOp = new DataOp(target.getName(), target.getDataType(), target.getValueType(), - OpOpData.PERSISTENTWRITE, hops.get(target.getName()), paramHops); - break; - - case RAND: - // We limit RAND_MIN, RAND_MAX, RAND_SPARSITY, RAND_SEED, and RAND_PDF to be constants - OpOpDG method = (paramHops.get(DataExpression.RAND_MIN).getValueType()==ValueType.STRING && + case READ: + currBuiltinOp = new DataOp(target.getName(), target.getDataType(), target.getValueType(), + OpOpData.PERSISTENTREAD, paramHops); + ((DataOp) currBuiltinOp) + .setFileName(((StringIdentifier) source.getVarParam(DataExpression.IO_FILENAME)).getValue()); + break; + + case WRITE: + currBuiltinOp = new DataOp(target.getName(), target.getDataType(), target.getValueType(), + OpOpData.PERSISTENTWRITE, hops.get(target.getName()), paramHops); + break; + + case RAND: + // We limit RAND_MIN, RAND_MAX, RAND_SPARSITY, RAND_SEED, and RAND_PDF to be constants + OpOpDG method = (paramHops.get(DataExpression.RAND_MIN).getValueType() == ValueType.STRING && target.getDataType() == DataType.MATRIX) ? OpOpDG.SINIT : OpOpDG.RAND; - currBuiltinOp = new DataGenOp(method, target, paramHops); - break; - - case FRAME: - // We limit RAND_MIN, RAND_MAX, RAND_SPARSITY, RAND_SEED, and RAND_PDF to be constants - method = OpOpDG.FRAMEINIT; - currBuiltinOp = new DataGenOp(method, target, paramHops); - break; - - case TENSOR: - case MATRIX: - ArrayList tmpMatrix = new ArrayList<>(); - tmpMatrix.add( 0, paramHops.get(DataExpression.RAND_DATA) ); - tmpMatrix.add( 1, paramHops.get(DataExpression.RAND_ROWS) ); - tmpMatrix.add( 2, paramHops.get(DataExpression.RAND_COLS) ); - tmpMatrix.add( 3, !paramHops.containsKey(DataExpression.RAND_DIMS) ? - new LiteralOp("-1") : paramHops.get(DataExpression.RAND_DIMS)); - tmpMatrix.add( 4, paramHops.get(DataExpression.RAND_BY_ROW) ); - currBuiltinOp = new ReorgOp(target.getName(), target.getDataType(), - target.getValueType(), ReOrgOp.RESHAPE, tmpMatrix); - break; - - case SQL: - currBuiltinOp = new DataOp(target.getName(), target.getDataType(), - target.getValueType(), OpOpData.SQLREAD, paramHops); - break; - - case FEDERATED: - currBuiltinOp = new DataOp(target.getName(), target.getDataType(), - target.getValueType(), OpOpData.FEDERATED, paramHops); - break; - - default: - throw new ParseException(source.printErrorLocation() + - "processDataExpression():: Unknown operation: " + source.getOpCode()); - } - - //set identifier meta data (incl dimensions and blocksizes) + currBuiltinOp = new DataGenOp(method, target, paramHops); + break; + + case FRAME: + // We limit RAND_MIN, RAND_MAX, RAND_SPARSITY, RAND_SEED, and RAND_PDF to be constants + method = OpOpDG.FRAMEINIT; + currBuiltinOp = new DataGenOp(method, target, paramHops); + break; + + case TENSOR: + case MATRIX: + ArrayList tmpMatrix = new ArrayList<>(); + tmpMatrix.add(0, paramHops.get(DataExpression.RAND_DATA)); + tmpMatrix.add(1, paramHops.get(DataExpression.RAND_ROWS)); + tmpMatrix.add(2, paramHops.get(DataExpression.RAND_COLS)); + tmpMatrix.add(3, !paramHops.containsKey(DataExpression.RAND_DIMS) ? new LiteralOp("-1") : paramHops + .get(DataExpression.RAND_DIMS)); + tmpMatrix.add(4, paramHops.get(DataExpression.RAND_BY_ROW)); + currBuiltinOp = new ReorgOp(target.getName(), target.getDataType(), target.getValueType(), + ReOrgOp.RESHAPE, tmpMatrix); + break; + + case SQL: + currBuiltinOp = new DataOp(target.getName(), target.getDataType(), target.getValueType(), + OpOpData.SQLREAD, paramHops); + break; + + case FEDERATED: + currBuiltinOp = new DataOp(target.getName(), target.getDataType(), target.getValueType(), + OpOpData.FEDERATED, paramHops); + break; + + default: + throw new ParseException(source.printErrorLocation() + "processDataExpression():: Unknown operation: " + + source.getOpCode()); + } + + // set identifier meta data (incl dimensions and blocksizes) setIdentifierParams(currBuiltinOp, source.getOutput()); - if( source.getOpCode()==DataExpression.DataOp.READ ) - ((DataOp)currBuiltinOp).setInputBlocksize(target.getBlocksize()); - else if ( source.getOpCode() == DataExpression.DataOp.WRITE ) { - ((DataOp)currBuiltinOp).setPrivacy(hops.get(target.getName()).getPrivacy()); - if( source.getVarParam(DataExpression.ROWBLOCKCOUNTPARAM) != null ) - currBuiltinOp.setBlocksize(Integer.parseInt( - source.getVarParam(DataExpression.ROWBLOCKCOUNTPARAM).toString())); + if(source.getOpCode() == DataExpression.DataOp.READ) + ((DataOp) currBuiltinOp).setInputBlocksize(target.getBlocksize()); + else if(source.getOpCode() == DataExpression.DataOp.WRITE) { + ((DataOp) currBuiltinOp).setPrivacy(hops.get(target.getName()).getPrivacy()); + if(source.getVarParam(DataExpression.ROWBLOCKCOUNTPARAM) != null) + currBuiltinOp + .setBlocksize(Integer.parseInt(source.getVarParam(DataExpression.ROWBLOCKCOUNTPARAM).toString())); } currBuiltinOp.setParseInfo(source); @@ -2202,25 +2237,24 @@ else if ( source.getOpCode() == DataExpression.DataOp.WRITE ) { } /** - * Construct HOps from parse tree: process BuiltinFunction Expressions in - * MultiAssignment Statements. For all other builtin function expressions, - * processBuiltinFunctionExpression() is used. + * Construct HOps from parse tree: process BuiltinFunction Expressions in MultiAssignment Statements. For all other + * builtin function expressions, processBuiltinFunctionExpression() is used. * - * @param source built-in function expression + * @param source built-in function expression * @param targetList list of data identifiers - * @param hops map of high-level operators + * @param hops map of high-level operators * @return high-level operator */ - private Hop processMultipleReturnBuiltinFunctionExpression(BuiltinFunctionExpression source, ArrayList targetList, - HashMap hops) { + private Hop processMultipleReturnBuiltinFunctionExpression(BuiltinFunctionExpression source, + ArrayList targetList, HashMap hops) { // Construct Hops for all inputs ArrayList inputs = new ArrayList<>(); - inputs.add( processExpression(source.getFirstExpr(), null, hops) ); + inputs.add(processExpression(source.getFirstExpr(), null, hops)); Expression[] expr = source.getAllExpr(); if(expr != null && expr.length > 1) { for(int i = 1; i < expr.length; i++) { - inputs.add( processExpression(expr[i], null, hops) ); + inputs.add(processExpression(expr[i], null, hops)); } } @@ -2233,7 +2267,7 @@ private Hop processMultipleReturnBuiltinFunctionExpression(BuiltinFunctionExpres // Construct Hop for current builtin function expression based on its type Hop currBuiltinOp = null; - switch (source.getOpCode()) { + switch(source.getOpCode()) { case QR: case LU: case EIGEN: @@ -2247,34 +2281,41 @@ private Hop processMultipleReturnBuiltinFunctionExpression(BuiltinFunctionExpres case SVD: // Number of outputs = size of targetList = #of identifiers in source.getOutputs - String[] outputNames = new String[targetList.size()]; - for ( int i=0; i < targetList.size(); i++ ) { + String[] outputNames = new String[targetList.size()]; + for(int i = 0; i < targetList.size(); i++) { outputNames[i] = targetList.get(i).getName(); - Hop output = new DataOp(outputNames[i], DataType.MATRIX, ValueType.FP64, inputs.get(0), OpOpData.FUNCTIONOUTPUT, inputs.get(0).getFilename()); + Hop output = new DataOp(outputNames[i], DataType.MATRIX, ValueType.FP64, inputs.get(0), + OpOpData.FUNCTIONOUTPUT, inputs.get(0).getFilename()); outputs.add(output); } // Create the hop for current function call - currBuiltinOp = new FunctionOp(ftype, nameSpace, source.getOpCode().toString(), null, inputs, outputNames, outputs); + currBuiltinOp = new FunctionOp(ftype, nameSpace, source.getOpCode().toString(), null, inputs, + outputNames, outputs); break; case COMPRESS: // Number of outputs = size of targetList = #of identifiers in source.getOutputs String[] outputNamesCompress = new String[targetList.size()]; outputNamesCompress[0] = targetList.get(0).getName(); outputNamesCompress[1] = targetList.get(1).getName(); - outputs.add(new DataOp(outputNamesCompress[0], DataType.MATRIX, ValueType.FP64, inputs.get(0), OpOpData.FUNCTIONOUTPUT, inputs.get(0).getFilename())); - outputs.add(new DataOp(outputNamesCompress[1], DataType.FRAME, ValueType.STRING, inputs.get(0), OpOpData.FUNCTIONOUTPUT, inputs.get(0).getFilename())); + outputs.add(new DataOp(outputNamesCompress[0], DataType.MATRIX, ValueType.FP64, inputs.get(0), + OpOpData.FUNCTIONOUTPUT, inputs.get(0).getFilename())); + outputs.add(new DataOp(outputNamesCompress[1], DataType.FRAME, ValueType.STRING, inputs.get(0), + OpOpData.FUNCTIONOUTPUT, inputs.get(0).getFilename())); // Create the hop for current function call - currBuiltinOp = new FunctionOp(ftype, nameSpace, source.getOpCode().toString(), null, inputs, outputNamesCompress, outputs); + currBuiltinOp = new FunctionOp(ftype, nameSpace, source.getOpCode().toString(), null, inputs, + outputNamesCompress, outputs); break; default: - throw new ParseException("Invaid Opcode in DMLTranslator:processMultipleReturnBuiltinFunctionExpression(): " + source.getOpCode()); + throw new ParseException( + "Invaid Opcode in DMLTranslator:processMultipleReturnBuiltinFunctionExpression(): " + + source.getOpCode()); } // set properties for created hops based on outputs of source expression - for ( int i=0; i < source.getOutputs().length; i++ ) { - setIdentifierParams( outputs.get(i), source.getOutputs()[i]); + for(int i = 0; i < source.getOutputs().length; i++) { + setIdentifierParams(outputs.get(i), source.getOutputs()[i]); outputs.get(i).setParseInfo(source); } currBuiltinOp.setParseInfo(source); @@ -2283,26 +2324,25 @@ private Hop processMultipleReturnBuiltinFunctionExpression(BuiltinFunctionExpres } /** - * Construct Hops from parse tree : Process BuiltinFunction Expression in an - * assignment statement + * Construct Hops from parse tree : Process BuiltinFunction Expression in an assignment statement * * @param source built-in function expression * @param target data identifier - * @param hops map of high-level operators + * @param hops map of high-level operators * @return high-level operator */ private Hop processBuiltinFunctionExpression(BuiltinFunctionExpression source, DataIdentifier target, - HashMap hops) { + HashMap hops) { Hop expr = null; - if(source.getFirstExpr() != null){ + if(source.getFirstExpr() != null) { expr = processExpression(source.getFirstExpr(), null, hops); } Hop expr2 = null; - if (source.getSecondExpr() != null) { + if(source.getSecondExpr() != null) { expr2 = processExpression(source.getSecondExpr(), null, hops); } Hop expr3 = null; - if (source.getThirdExpr() != null) { + if(source.getThirdExpr() != null) { expr3 = processExpression(source.getThirdExpr(), null, hops); } @@ -2310,506 +2350,519 @@ private Hop processBuiltinFunctionExpression(BuiltinFunctionExpression source, D target = (target == null) ? createTarget(source) : target; // Construct the hop based on the type of Builtin function - switch (source.getOpCode()) { - - case EVAL: - case EVALLIST: - currBuiltinOp = new NaryOp(target.getName(), target.getDataType(), target.getValueType(), - OpOpN.EVAL, processAllExpressions(source.getAllExpr(), hops)); - break; - - case COLSUM: - case COLMAX: - case COLMIN: - case COLMEAN: - case COLPROD: - case COLVAR: - currBuiltinOp = new AggUnaryOp(target.getName(), DataType.MATRIX, target.getValueType(), - AggOp.valueOf(source.getOpCode().name().substring(3)), Direction.Col, expr); - break; - - case COLSD: - // colStdDevs = sqrt(colVariances) - currBuiltinOp = new AggUnaryOp(target.getName(), DataType.MATRIX, - target.getValueType(), AggOp.VAR, Direction.Col, expr); - currBuiltinOp = new UnaryOp(target.getName(), DataType.MATRIX, - target.getValueType(), OpOp1.SQRT, currBuiltinOp); - break; - - case ROWSUM: - case ROWMIN: - case ROWMAX: - case ROWMEAN: - case ROWPROD: - case ROWVAR: - currBuiltinOp = new AggUnaryOp(target.getName(), DataType.MATRIX, target.getValueType(), - AggOp.valueOf(source.getOpCode().name().substring(3)), Direction.Row, expr); - break; - - case ROWINDEXMAX: - currBuiltinOp = new AggUnaryOp(target.getName(), DataType.MATRIX, target.getValueType(), AggOp.MAXINDEX, + switch(source.getOpCode()) { + + case EVAL: + case EVALLIST: + currBuiltinOp = new NaryOp(target.getName(), target.getDataType(), target.getValueType(), OpOpN.EVAL, + processAllExpressions(source.getAllExpr(), hops)); + break; + + case COLSUM: + case COLMAX: + case COLMIN: + case COLMEAN: + case COLPROD: + case COLVAR: + currBuiltinOp = new AggUnaryOp(target.getName(), DataType.MATRIX, target.getValueType(), + AggOp.valueOf(source.getOpCode().name().substring(3)), Direction.Col, expr); + break; + + case COLSD: + // colStdDevs = sqrt(colVariances) + currBuiltinOp = new AggUnaryOp(target.getName(), DataType.MATRIX, target.getValueType(), AggOp.VAR, + Direction.Col, expr); + currBuiltinOp = new UnaryOp(target.getName(), DataType.MATRIX, target.getValueType(), OpOp1.SQRT, + currBuiltinOp); + break; + + case ROWSUM: + case ROWMIN: + case ROWMAX: + case ROWMEAN: + case ROWPROD: + case ROWVAR: + currBuiltinOp = new AggUnaryOp(target.getName(), DataType.MATRIX, target.getValueType(), + AggOp.valueOf(source.getOpCode().name().substring(3)), Direction.Row, expr); + break; + + case ROWINDEXMAX: + currBuiltinOp = new AggUnaryOp(target.getName(), DataType.MATRIX, target.getValueType(), AggOp.MAXINDEX, Direction.Row, expr); - break; + break; - case ROWINDEXMIN: - currBuiltinOp = new AggUnaryOp(target.getName(), DataType.MATRIX, target.getValueType(), AggOp.MININDEX, + case ROWINDEXMIN: + currBuiltinOp = new AggUnaryOp(target.getName(), DataType.MATRIX, target.getValueType(), AggOp.MININDEX, Direction.Row, expr); - break; - - case ROWSD: - // rowStdDevs = sqrt(rowVariances) - currBuiltinOp = new AggUnaryOp(target.getName(), DataType.MATRIX, - target.getValueType(), AggOp.VAR, Direction.Row, expr); - currBuiltinOp = new UnaryOp(target.getName(), DataType.MATRIX, - target.getValueType(), OpOp1.SQRT, currBuiltinOp); - break; - - case NROW: - // If the dimensions are available at compile time, then create a LiteralOp (constant propagation) - // Else create a UnaryOp so that a control program instruction is generated - currBuiltinOp = (expr.getDim1()==-1) ? new UnaryOp(target.getName(), target.getDataType(), - target.getValueType(), OpOp1.NROW, expr) : new LiteralOp(expr.getDim1()); - break; - case NCOL: - // If the dimensions are available at compile time, then create a LiteralOp (constant propagation) - // Else create a UnaryOp so that a control program instruction is generated - currBuiltinOp = (expr.getDim2()==-1) ? new UnaryOp(target.getName(), target.getDataType(), - target.getValueType(), OpOp1.NCOL, expr) : new LiteralOp(expr.getDim2()); - break; - case LENGTH: - // If the dimensions are available at compile time, then create a LiteralOp (constant propagation) - // Else create a UnaryOp so that a control program instruction is generated - currBuiltinOp = (expr.getDim1()==-1 || expr.getDim2()==-1) ? new UnaryOp(target.getName(), target.getDataType(), - target.getValueType(), OpOp1.LENGTH, expr) : new LiteralOp(expr.getDim1()*expr.getDim2()); - break; - - case LINEAGE: - //construct hop and enable lineage tracing if necessary - currBuiltinOp = new UnaryOp(target.getName(), target.getDataType(), - target.getValueType(), OpOp1.LINEAGE, expr); - DMLScript.LINEAGE = true; - break; - - case LIST: - currBuiltinOp = new NaryOp(target.getName(), DataType.LIST, ValueType.UNKNOWN, - OpOpN.LIST, processAllExpressions(source.getAllExpr(), hops)); - break; - - case EXISTS: - currBuiltinOp = new UnaryOp(target.getName(), DataType.SCALAR, - target.getValueType(), OpOp1.EXISTS, expr); - break; - - case SUM: - case PROD: - case VAR: - currBuiltinOp = new AggUnaryOp(target.getName(), DataType.SCALAR, target.getValueType(), - AggOp.valueOf(source.getOpCode().name()), Direction.RowCol, expr); - break; + break; - case MEAN: - if ( expr2 == null ) { - // example: x = mean(Y); - currBuiltinOp = new AggUnaryOp(target.getName(), DataType.SCALAR, target.getValueType(), AggOp.MEAN, - Direction.RowCol, expr); - } - else { - // example: x = mean(Y,W); - // stable weighted mean is implemented by using centralMoment with order = 0 - Hop orderHop = new LiteralOp(0); - currBuiltinOp=new TernaryOp(target.getName(), DataType.SCALAR, - target.getValueType(), OpOp3.MOMENT, expr, expr2, orderHop); - } - break; - - case SD: - // stdDev = sqrt(variance) - currBuiltinOp = new AggUnaryOp(target.getName(), DataType.SCALAR, - target.getValueType(), AggOp.VAR, Direction.RowCol, expr); - HopRewriteUtils.setOutputParametersForScalar(currBuiltinOp); - currBuiltinOp = new UnaryOp(target.getName(), DataType.SCALAR, - target.getValueType(), OpOp1.SQRT, currBuiltinOp); - break; - - case MIN: - case MAX: - //construct AggUnary for min(X) but BinaryOp for min(X,Y) and NaryOp for min(X,Y,Z) - currBuiltinOp = (expr2 == null) ? - new AggUnaryOp(target.getName(), DataType.SCALAR, target.getValueType(), - AggOp.valueOf(source.getOpCode().name()), Direction.RowCol, expr) : - (source.getAllExpr().length == 2) ? - new BinaryOp(target.getName(), target.getDataType(), target.getValueType(), - OpOp2.valueOf(source.getOpCode().name()), expr, expr2) : - new NaryOp(target.getName(), target.getDataType(), target.getValueType(), - OpOpN.valueOf(source.getOpCode().name()), processAllExpressions(source.getAllExpr(), hops)); - break; - - case PPRED: - String sop = ((StringIdentifier)source.getThirdExpr()).getValue(); - sop = sop.replace("\"", ""); - OpOp2 operation; - if ( sop.equalsIgnoreCase(">=") ) - operation = OpOp2.GREATEREQUAL; - else if ( sop.equalsIgnoreCase(">") ) - operation = OpOp2.GREATER; - else if ( sop.equalsIgnoreCase("<=") ) - operation = OpOp2.LESSEQUAL; - else if ( sop.equalsIgnoreCase("<") ) - operation = OpOp2.LESS; - else if ( sop.equalsIgnoreCase("==") ) - operation = OpOp2.EQUAL; - else if ( sop.equalsIgnoreCase("!=") ) - operation = OpOp2.NOTEQUAL; - else { - throw new ParseException(source.printErrorLocation() + "Unknown argument (" + sop + ") for PPRED."); - } - currBuiltinOp = new BinaryOp(target.getName(), target.getDataType(), target.getValueType(), operation, expr, expr2); - break; + case ROWSD: + // rowStdDevs = sqrt(rowVariances) + currBuiltinOp = new AggUnaryOp(target.getName(), DataType.MATRIX, target.getValueType(), AggOp.VAR, + Direction.Row, expr); + currBuiltinOp = new UnaryOp(target.getName(), DataType.MATRIX, target.getValueType(), OpOp1.SQRT, + currBuiltinOp); + break; - case TRACE: - currBuiltinOp = new AggUnaryOp(target.getName(), DataType.SCALAR, target.getValueType(), AggOp.TRACE, - Direction.RowCol, expr); - break; - - case TRANS: - case DIAG: - case REV: - currBuiltinOp = new ReorgOp(target.getName(), DataType.MATRIX, - target.getValueType(), ReOrgOp.valueOf(source.getOpCode().name()), expr); - break; - - case CBIND: - case RBIND: - OpOp2 appendOp2 = (source.getOpCode()==Builtins.CBIND) ? OpOp2.CBIND : OpOp2.RBIND; - OpOpN appendOpN = (source.getOpCode()==Builtins.CBIND) ? OpOpN.CBIND : OpOpN.RBIND; - currBuiltinOp = (source.getAllExpr().length == 2) ? - new BinaryOp(target.getName(), target.getDataType(), target.getValueType(), appendOp2, expr, expr2) : - new NaryOp(target.getName(), target.getDataType(), target.getValueType(), appendOpN, + case NROW: + // If the dimensions are available at compile time, then create a LiteralOp (constant propagation) + // Else create a UnaryOp so that a control program instruction is generated + currBuiltinOp = (expr.getDim1() == -1) ? new UnaryOp(target.getName(), target.getDataType(), + target.getValueType(), OpOp1.NROW, expr) : new LiteralOp(expr.getDim1()); + break; + case NCOL: + // If the dimensions are available at compile time, then create a LiteralOp (constant propagation) + // Else create a UnaryOp so that a control program instruction is generated + currBuiltinOp = (expr.getDim2() == -1) ? new UnaryOp(target.getName(), target.getDataType(), + target.getValueType(), OpOp1.NCOL, expr) : new LiteralOp(expr.getDim2()); + break; + case LENGTH: + // If the dimensions are available at compile time, then create a LiteralOp (constant propagation) + // Else create a UnaryOp so that a control program instruction is generated + currBuiltinOp = (expr.getDim1() == -1 || expr.getDim2() == -1) ? new UnaryOp(target.getName(), + target.getDataType(), target.getValueType(), OpOp1.LENGTH, + expr) : new LiteralOp(expr.getDim1() * expr.getDim2()); + break; + + case LINEAGE: + // construct hop and enable lineage tracing if necessary + currBuiltinOp = new UnaryOp(target.getName(), target.getDataType(), target.getValueType(), + OpOp1.LINEAGE, expr); + DMLScript.LINEAGE = true; + break; + + case LIST: + currBuiltinOp = new NaryOp(target.getName(), DataType.LIST, ValueType.UNKNOWN, OpOpN.LIST, processAllExpressions(source.getAllExpr(), hops)); - break; - - case TABLE: - - // Always a TertiaryOp is created for table(). - // - create a hop for weights, if not provided in the function call. - int numTableArgs = source._args.length; - - switch(numTableArgs) { - case 2: - case 4: - // example DML statement: F = ctable(A,B) or F = ctable(A,B,10,15) - // here, weight is interpreted as 1.0 - Hop weightHop = new LiteralOp(1.0); - // set dimensions - weightHop.setDim1(0); - weightHop.setDim2(0); - weightHop.setNnz(-1); - weightHop.setBlocksize(0); - - if ( numTableArgs == 2 ) - currBuiltinOp = new TernaryOp(target.getName(), target.getDataType(), target.getValueType(), OpOp3.CTABLE, expr, expr2, weightHop); + break; + + case EXISTS: + currBuiltinOp = new UnaryOp(target.getName(), DataType.SCALAR, target.getValueType(), OpOp1.EXISTS, + expr); + break; + + case SUM: + case PROD: + case VAR: + currBuiltinOp = new AggUnaryOp(target.getName(), DataType.SCALAR, target.getValueType(), + AggOp.valueOf(source.getOpCode().name()), Direction.RowCol, expr); + break; + + case MEAN: + if(expr2 == null) { + // example: x = mean(Y); + currBuiltinOp = new AggUnaryOp(target.getName(), DataType.SCALAR, target.getValueType(), AggOp.MEAN, + Direction.RowCol, expr); + } else { - Hop outDim1 = processExpression(source._args[2], null, hops); - Hop outDim2 = processExpression(source._args[3], null, hops); - currBuiltinOp = new TernaryOp(target.getName(), target.getDataType(), target.getValueType(), - OpOp3.CTABLE, expr, expr2, weightHop, outDim1, outDim2, new LiteralOp(true)); + // example: x = mean(Y,W); + // stable weighted mean is implemented by using centralMoment with order = 0 + Hop orderHop = new LiteralOp(0); + currBuiltinOp = new TernaryOp(target.getName(), DataType.SCALAR, target.getValueType(), + OpOp3.MOMENT, expr, expr2, orderHop); } break; - case 3: - case 5: - case 6: - // example DML statement: F = ctable(A,B,W) or F = ctable(A,B,W,10,15) - if (numTableArgs == 3) - currBuiltinOp = new TernaryOp(target.getName(), target.getDataType(), target.getValueType(), OpOp3.CTABLE, expr, expr2, expr3); + case SD: + // stdDev = sqrt(variance) + currBuiltinOp = new AggUnaryOp(target.getName(), DataType.SCALAR, target.getValueType(), AggOp.VAR, + Direction.RowCol, expr); + HopRewriteUtils.setOutputParametersForScalar(currBuiltinOp); + currBuiltinOp = new UnaryOp(target.getName(), DataType.SCALAR, target.getValueType(), OpOp1.SQRT, + currBuiltinOp); + break; + + case MIN: + case MAX: + // construct AggUnary for min(X) but BinaryOp for min(X,Y) and NaryOp for min(X,Y,Z) + currBuiltinOp = (expr2 == null) ? new AggUnaryOp(target.getName(), DataType.SCALAR, + target.getValueType(), AggOp.valueOf(source.getOpCode().name()), Direction.RowCol, + expr) : (source.getAllExpr().length == 2) ? new BinaryOp(target.getName(), target.getDataType(), + target.getValueType(), OpOp2.valueOf(source.getOpCode().name()), expr, + expr2) : new NaryOp(target.getName(), target.getDataType(), target.getValueType(), + OpOpN.valueOf(source.getOpCode().name()), processAllExpressions(source.getAllExpr(), hops)); + break; + + case PPRED: + String sop = ((StringIdentifier) source.getThirdExpr()).getValue(); + sop = sop.replace("\"", ""); + OpOp2 operation; + if(sop.equalsIgnoreCase(">=")) + operation = OpOp2.GREATEREQUAL; + else if(sop.equalsIgnoreCase(">")) + operation = OpOp2.GREATER; + else if(sop.equalsIgnoreCase("<=")) + operation = OpOp2.LESSEQUAL; + else if(sop.equalsIgnoreCase("<")) + operation = OpOp2.LESS; + else if(sop.equalsIgnoreCase("==")) + operation = OpOp2.EQUAL; + else if(sop.equalsIgnoreCase("!=")) + operation = OpOp2.NOTEQUAL; else { - Hop outDim1 = processExpression(source._args[3], null, hops); - Hop outDim2 = processExpression(source._args[4], null, hops); - Hop outputEmptyBlocks = numTableArgs == 6 ? - processExpression(source._args[5], null, hops) : new LiteralOp(true); - currBuiltinOp = new TernaryOp(target.getName(), target.getDataType(), target.getValueType(), - OpOp3.CTABLE, expr, expr2, expr3, outDim1, outDim2, outputEmptyBlocks); + throw new ParseException(source.printErrorLocation() + "Unknown argument (" + sop + ") for PPRED."); } + currBuiltinOp = new BinaryOp(target.getName(), target.getDataType(), target.getValueType(), operation, + expr, expr2); break; - default: - throw new ParseException("Invalid number of arguments "+ numTableArgs + " to table() function."); - } - break; + case TRACE: + currBuiltinOp = new AggUnaryOp(target.getName(), DataType.SCALAR, target.getValueType(), AggOp.TRACE, + Direction.RowCol, expr); + break; - //data type casts - case CAST_AS_SCALAR: - currBuiltinOp = new UnaryOp(target.getName(), DataType.SCALAR, target.getValueType(), OpOp1.CAST_AS_SCALAR, expr); - break; - case CAST_AS_MATRIX: - currBuiltinOp = new UnaryOp(target.getName(), DataType.MATRIX, target.getValueType(), OpOp1.CAST_AS_MATRIX, expr); - break; - case CAST_AS_FRAME: - if(expr2 != null) - currBuiltinOp = new BinaryOp(target.getName(), DataType.FRAME, target.getValueType(), OpOp2.CAST_AS_FRAME, expr, expr2); - else - currBuiltinOp = new UnaryOp(target.getName(), DataType.FRAME, target.getValueType(), OpOp1.CAST_AS_FRAME, expr); - break; - case CAST_AS_LIST: - currBuiltinOp = new UnaryOp(target.getName(), DataType.LIST, target.getValueType(), OpOp1.CAST_AS_LIST, expr); - break; - - //value type casts - case CAST_AS_DOUBLE: - currBuiltinOp = new UnaryOp(target.getName(), target.getDataType(), ValueType.FP64, OpOp1.CAST_AS_DOUBLE, expr); - break; - case CAST_AS_INT: - currBuiltinOp = new UnaryOp(target.getName(), target.getDataType(), ValueType.INT64, OpOp1.CAST_AS_INT, expr); - break; - case CAST_AS_BOOLEAN: - currBuiltinOp = new UnaryOp(target.getName(), target.getDataType(), ValueType.BOOLEAN, OpOp1.CAST_AS_BOOLEAN, expr); - break; - case LOCAL: - currBuiltinOp = new UnaryOp(target.getName(), target.getDataType(), ValueType.FP64, OpOp1.LOCAL, expr); - break; - case COMPRESS: - currBuiltinOp = new UnaryOp(target.getName(), target.getDataType(), ValueType.FP64, OpOp1.COMPRESS, expr); - break; - case DECOMPRESS: - currBuiltinOp = new UnaryOp(target.getName(), target.getDataType(), ValueType.FP64, OpOp1.DECOMPRESS, expr); - break; - - // Boolean binary - case XOR: - case BITWAND: - case BITWOR: - case BITWXOR: - case BITWSHIFTL: - case BITWSHIFTR: - currBuiltinOp = new BinaryOp(target.getName(), target.getDataType(), - target.getValueType(), OpOp2.valueOf(source.getOpCode().name()), expr, expr2); - break; - case ABS: - case SIN: - case COS: - case TAN: - case ASIN: - case ACOS: - case ATAN: - case SINH: - case COSH: - case TANH: - case SIGN: - case SQRT: - case EXP: - case ROUND: - case CEIL: - case FLOOR: - case CUMSUM: - case CUMPROD: - case CUMSUMPROD: - case CUMMIN: - case CUMMAX: - case ISNA: - case ISNAN: - case ISINF: - currBuiltinOp = new UnaryOp(target.getName(), target.getDataType(), target.getValueType(), - OpOp1.valueOf(source.getOpCode().name()), expr); - break; - case DROP_INVALID_TYPE: - case DROP_INVALID_LENGTH: - case VALUE_SWAP: - case FRAME_ROW_REPLICATE: - case APPLY_SCHEMA: - currBuiltinOp = new BinaryOp(target.getName(), target.getDataType(), - target.getValueType(), OpOp2.valueOf(source.getOpCode().name()), expr, expr2); - break; - case MAP: - currBuiltinOp = new TernaryOp(target.getName(), target.getDataType(), - target.getValueType(), OpOp3.valueOf(source.getOpCode().name()), - expr, expr2, (expr3==null) ? new LiteralOp(0L) : expr3); - break; - - case LOG: - if (expr2 == null) { - OpOp1 mathOp2; - switch (source.getOpCode()) { - case LOG: - mathOp2 = OpOp1.LOG; + case TRANS: + case DIAG: + case REV: + currBuiltinOp = new ReorgOp(target.getName(), DataType.MATRIX, target.getValueType(), + ReOrgOp.valueOf(source.getOpCode().name()), expr); + break; + + case CBIND: + case RBIND: + OpOp2 appendOp2 = (source.getOpCode() == Builtins.CBIND) ? OpOp2.CBIND : OpOp2.RBIND; + OpOpN appendOpN = (source.getOpCode() == Builtins.CBIND) ? OpOpN.CBIND : OpOpN.RBIND; + currBuiltinOp = (source.getAllExpr().length == 2) ? new BinaryOp(target.getName(), target.getDataType(), + target.getValueType(), appendOp2, expr, expr2) : new NaryOp(target.getName(), target.getDataType(), + target.getValueType(), appendOpN, processAllExpressions(source.getAllExpr(), hops)); + break; + + case TABLE: + + // Always a TertiaryOp is created for table(). + // - create a hop for weights, if not provided in the function call. + int numTableArgs = source._args.length; + + switch(numTableArgs) { + case 2: + case 4: + // example DML statement: F = ctable(A,B) or F = ctable(A,B,10,15) + // here, weight is interpreted as 1.0 + Hop weightHop = new LiteralOp(1.0); + // set dimensions + weightHop.setDim1(0); + weightHop.setDim2(0); + weightHop.setNnz(-1); + weightHop.setBlocksize(0); + + if(numTableArgs == 2) + currBuiltinOp = new TernaryOp(target.getName(), target.getDataType(), target.getValueType(), + OpOp3.CTABLE, expr, expr2, weightHop); + else { + Hop outDim1 = processExpression(source._args[2], null, hops); + Hop outDim2 = processExpression(source._args[3], null, hops); + currBuiltinOp = new TernaryOp(target.getName(), target.getDataType(), target.getValueType(), + OpOp3.CTABLE, expr, expr2, weightHop, outDim1, outDim2, new LiteralOp(true)); + } break; + + case 3: + case 5: + case 6: + // example DML statement: F = ctable(A,B,W) or F = ctable(A,B,W,10,15) + if(numTableArgs == 3) + currBuiltinOp = new TernaryOp(target.getName(), target.getDataType(), target.getValueType(), + OpOp3.CTABLE, expr, expr2, expr3); + else { + Hop outDim1 = processExpression(source._args[3], null, hops); + Hop outDim2 = processExpression(source._args[4], null, hops); + Hop outputEmptyBlocks = numTableArgs == 6 ? processExpression(source._args[5], null, + hops) : new LiteralOp(true); + currBuiltinOp = new TernaryOp(target.getName(), target.getDataType(), target.getValueType(), + OpOp3.CTABLE, expr, expr2, expr3, outDim1, outDim2, outputEmptyBlocks); + } + break; + default: - throw new ParseException(source.printErrorLocation() + - "processBuiltinFunctionExpression():: Could not find Operation type for builtin function: " - + source.getOpCode()); + throw new ParseException( + "Invalid number of arguments " + numTableArgs + " to table() function."); + } + break; + + // data type casts + case CAST_AS_SCALAR: + currBuiltinOp = new UnaryOp(target.getName(), DataType.SCALAR, target.getValueType(), + OpOp1.CAST_AS_SCALAR, expr); + break; + case CAST_AS_MATRIX: + currBuiltinOp = new UnaryOp(target.getName(), DataType.MATRIX, target.getValueType(), + OpOp1.CAST_AS_MATRIX, expr); + break; + case CAST_AS_FRAME: + if(expr2 != null) + currBuiltinOp = new BinaryOp(target.getName(), DataType.FRAME, target.getValueType(), + OpOp2.CAST_AS_FRAME, expr, expr2); + else + currBuiltinOp = new UnaryOp(target.getName(), DataType.FRAME, target.getValueType(), + OpOp1.CAST_AS_FRAME, expr); + break; + case CAST_AS_LIST: + currBuiltinOp = new UnaryOp(target.getName(), DataType.LIST, target.getValueType(), OpOp1.CAST_AS_LIST, + expr); + break; + + // value type casts + case CAST_AS_DOUBLE: + currBuiltinOp = new UnaryOp(target.getName(), target.getDataType(), ValueType.FP64, + OpOp1.CAST_AS_DOUBLE, expr); + break; + case CAST_AS_INT: + currBuiltinOp = new UnaryOp(target.getName(), target.getDataType(), ValueType.INT64, OpOp1.CAST_AS_INT, + expr); + break; + case CAST_AS_BOOLEAN: + currBuiltinOp = new UnaryOp(target.getName(), target.getDataType(), ValueType.BOOLEAN, + OpOp1.CAST_AS_BOOLEAN, expr); + break; + case LOCAL: + currBuiltinOp = new UnaryOp(target.getName(), target.getDataType(), ValueType.FP64, OpOp1.LOCAL, expr); + break; + case COMPRESS: + currBuiltinOp = new UnaryOp(target.getName(), target.getDataType(), ValueType.FP64, OpOp1.COMPRESS, + expr); + break; + case DECOMPRESS: + currBuiltinOp = new UnaryOp(target.getName(), target.getDataType(), ValueType.FP64, OpOp1.DECOMPRESS, + expr); + break; + + // Boolean binary + case XOR: + case BITWAND: + case BITWOR: + case BITWXOR: + case BITWSHIFTL: + case BITWSHIFTR: + currBuiltinOp = new BinaryOp(target.getName(), target.getDataType(), target.getValueType(), + OpOp2.valueOf(source.getOpCode().name()), expr, expr2); + break; + case ABS: + case SIN: + case COS: + case TAN: + case ASIN: + case ACOS: + case ATAN: + case SINH: + case COSH: + case TANH: + case SIGN: + case SQRT: + case EXP: + case ROUND: + case CEIL: + case FLOOR: + case CUMSUM: + case CUMPROD: + case CUMSUMPROD: + case CUMMIN: + case CUMMAX: + case ISNA: + case ISNAN: + case ISINF: + currBuiltinOp = new UnaryOp(target.getName(), target.getDataType(), target.getValueType(), + OpOp1.valueOf(source.getOpCode().name()), expr); + break; + case DROP_INVALID_TYPE: + case DROP_INVALID_LENGTH: + case VALUE_SWAP: + case FRAME_ROW_REPLICATE: + case APPLY_SCHEMA: + currBuiltinOp = new BinaryOp(target.getName(), target.getDataType(), target.getValueType(), + OpOp2.valueOf(source.getOpCode().name()), expr, expr2); + break; + case MAP: + currBuiltinOp = new TernaryOp(target.getName(), target.getDataType(), target.getValueType(), + OpOp3.valueOf(source.getOpCode().name()), expr, expr2, (expr3 == null) ? new LiteralOp(0L) : expr3); + break; + + case LOG: + if(expr2 == null) { + OpOp1 mathOp2; + switch(source.getOpCode()) { + case LOG: + mathOp2 = OpOp1.LOG; + break; + default: + throw new ParseException(source.printErrorLocation() + + "processBuiltinFunctionExpression():: Could not find Operation type for builtin function: " + + source.getOpCode()); } - currBuiltinOp = new UnaryOp(target.getName(), - target.getDataType(), target.getValueType(), mathOp2, expr); - } else { + currBuiltinOp = new UnaryOp(target.getName(), target.getDataType(), target.getValueType(), mathOp2, + expr); + } + else { OpOp2 mathOp3; - switch (source.getOpCode()) { - case LOG: - mathOp3 = OpOp2.LOG; - break; - default: - throw new ParseException(source.printErrorLocation() + - "processBuiltinFunctionExpression():: Could not find Operation type for builtin function: " + switch(source.getOpCode()) { + case LOG: + mathOp3 = OpOp2.LOG; + break; + default: + throw new ParseException(source.printErrorLocation() + + "processBuiltinFunctionExpression():: Could not find Operation type for builtin function: " + source.getOpCode()); } currBuiltinOp = new BinaryOp(target.getName(), target.getDataType(), target.getValueType(), mathOp3, - expr, expr2); + expr, expr2); } - break; - - case MOMENT: - case COV: - case QUANTILE: - case INTERQUANTILE: - currBuiltinOp = (expr3 == null) ? new BinaryOp(target.getName(), target.getDataType(), target.getValueType(), - OpOp2.valueOf(source.getOpCode().name()), expr, expr2) : new TernaryOp(target.getName(), target.getDataType(), - target.getValueType(), OpOp3.valueOf(source.getOpCode().name()), expr, expr2,expr3); - break; - - case IQM: - case MEDIAN: - currBuiltinOp = (expr2 == null) ? new UnaryOp(target.getName(), target.getDataType(), target.getValueType(), - OpOp1.valueOf(source.getOpCode().name()), expr) : new BinaryOp(target.getName(), target.getDataType(), - target.getValueType(), OpOp2.valueOf(source.getOpCode().name()), expr, expr2); - break; - - case IFELSE: - currBuiltinOp=new TernaryOp(target.getName(), target.getDataType(), - target.getValueType(), OpOp3.IFELSE, expr, expr2, expr3); - break; - - case SEQ: - HashMap randParams = new HashMap<>(); - randParams.put(Statement.SEQ_FROM, expr); - randParams.put(Statement.SEQ_TO, expr2); - randParams.put(Statement.SEQ_INCR, (expr3!=null)?expr3 : new LiteralOp(1)); - //note incr: default -1 (for from>to) handled during runtime - currBuiltinOp = new DataGenOp(OpOpDG.SEQ, target, randParams); - break; - - case TIME: - currBuiltinOp = new DataGenOp(OpOpDG.TIME, target); - break; - - case SAMPLE: - { - Expression[] in = source.getAllExpr(); - - // arguments: range/size/replace/seed; defaults: replace=FALSE - - HashMap tmpparams = new HashMap<>(); - tmpparams.put(DataExpression.RAND_MAX, expr); //range - tmpparams.put(DataExpression.RAND_ROWS, expr2); - tmpparams.put(DataExpression.RAND_COLS, new LiteralOp(1)); - - if ( in.length == 4 ) - { - tmpparams.put(DataExpression.RAND_PDF, expr3); - Hop seed = processExpression(in[3], null, hops); - tmpparams.put(DataExpression.RAND_SEED, seed); - } - else if ( in.length == 3 ) - { - // check if the third argument is "replace" or "seed" - if ( expr3.getValueType() == ValueType.BOOLEAN ) - { + break; + + case MOMENT: + case COV: + case QUANTILE: + case INTERQUANTILE: + currBuiltinOp = (expr3 == null) ? new BinaryOp(target.getName(), target.getDataType(), + target.getValueType(), OpOp2.valueOf(source.getOpCode().name()), expr, + expr2) : new TernaryOp(target.getName(), target.getDataType(), target.getValueType(), + OpOp3.valueOf(source.getOpCode().name()), expr, expr2, expr3); + break; + + case IQM: + case MEDIAN: + currBuiltinOp = (expr2 == null) ? new UnaryOp(target.getName(), target.getDataType(), + target.getValueType(), OpOp1.valueOf(source.getOpCode().name()), + expr) : new BinaryOp(target.getName(), target.getDataType(), target.getValueType(), + OpOp2.valueOf(source.getOpCode().name()), expr, expr2); + break; + + case IFELSE: + currBuiltinOp = new TernaryOp(target.getName(), target.getDataType(), target.getValueType(), + OpOp3.IFELSE, expr, expr2, expr3); + break; + + case SEQ: + HashMap randParams = new HashMap<>(); + randParams.put(Statement.SEQ_FROM, expr); + randParams.put(Statement.SEQ_TO, expr2); + randParams.put(Statement.SEQ_INCR, (expr3 != null) ? expr3 : new LiteralOp(1)); + // note incr: default -1 (for from>to) handled during runtime + currBuiltinOp = new DataGenOp(OpOpDG.SEQ, target, randParams); + break; + + case TIME: + currBuiltinOp = new DataGenOp(OpOpDG.TIME, target); + break; + + case SAMPLE: { + Expression[] in = source.getAllExpr(); + + // arguments: range/size/replace/seed; defaults: replace=FALSE + + HashMap tmpparams = new HashMap<>(); + tmpparams.put(DataExpression.RAND_MAX, expr); // range + tmpparams.put(DataExpression.RAND_ROWS, expr2); + tmpparams.put(DataExpression.RAND_COLS, new LiteralOp(1)); + + if(in.length == 4) { tmpparams.put(DataExpression.RAND_PDF, expr3); - tmpparams.put(DataExpression.RAND_SEED, new LiteralOp(DataGenOp.UNSPECIFIED_SEED) ); + Hop seed = processExpression(in[3], null, hops); + tmpparams.put(DataExpression.RAND_SEED, seed); } - else if ( expr3.getValueType() == ValueType.INT64 ) - { + else if(in.length == 3) { + // check if the third argument is "replace" or "seed" + if(expr3.getValueType() == ValueType.BOOLEAN) { + tmpparams.put(DataExpression.RAND_PDF, expr3); + tmpparams.put(DataExpression.RAND_SEED, new LiteralOp(DataGenOp.UNSPECIFIED_SEED)); + } + else if(expr3.getValueType() == ValueType.INT64) { + tmpparams.put(DataExpression.RAND_PDF, new LiteralOp(false)); + tmpparams.put(DataExpression.RAND_SEED, expr3); + } + else + throw new HopsException("Invalid input type " + expr3.getValueType() + " in sample()."); + + } + else if(in.length == 2) { tmpparams.put(DataExpression.RAND_PDF, new LiteralOp(false)); - tmpparams.put(DataExpression.RAND_SEED, expr3 ); + tmpparams.put(DataExpression.RAND_SEED, new LiteralOp(DataGenOp.UNSPECIFIED_SEED)); } - else - throw new HopsException("Invalid input type " + expr3.getValueType() + " in sample()."); - - } - else if ( in.length == 2 ) - { - tmpparams.put(DataExpression.RAND_PDF, new LiteralOp(false)); - tmpparams.put(DataExpression.RAND_SEED, new LiteralOp(DataGenOp.UNSPECIFIED_SEED) ); - } - - currBuiltinOp = new DataGenOp(OpOpDG.SAMPLE, target, tmpparams); - break; - } - - case SOLVE: - currBuiltinOp = new BinaryOp(target.getName(), target.getDataType(), target.getValueType(), OpOp2.SOLVE, expr, expr2); - break; - - case INVERSE: - case CHOLESKY: - case TYPEOF: - case DETECTSCHEMA: - case COLNAMES: - currBuiltinOp = new UnaryOp(target.getName(), target.getDataType(), - target.getValueType(), OpOp1.valueOf(source.getOpCode().name()), expr); - break; - - case OUTER: - if( !(expr3 instanceof LiteralOp) ) - throw new HopsException("Operator for outer builtin function must be a constant: "+expr3); - OpOp2 op = OpOp2.valueOfByOpcode(((LiteralOp)expr3).getStringValue()); - if( op == null ) - throw new HopsException("Unsupported outer vector binary operation: "+((LiteralOp)expr3).getStringValue()); - - currBuiltinOp = new BinaryOp(target.getName(), DataType.MATRIX, target.getValueType(), op, expr, expr2); - ((BinaryOp)currBuiltinOp).setOuterVectorOperation(true); //flag op as specific outer vector operation - currBuiltinOp.refreshSizeInformation(); //force size reevaluation according to 'outer' flag otherwise danger of incorrect dims - break; - - case BIASADD: - case BIASMULT: { - ArrayList inHops1 = new ArrayList<>(); - inHops1.add(expr); - inHops1.add(expr2); - currBuiltinOp = new DnnOp(target.getName(), DataType.MATRIX, target.getValueType(), - OpOpDnn.valueOf(source.getOpCode().name()), inHops1); - setBlockSizeAndRefreshSizeInfo(expr, currBuiltinOp); - break; - } - case AVG_POOL: - case MAX_POOL: { - currBuiltinOp = new DnnOp(target.getName(), DataType.MATRIX, target.getValueType(), - OpOpDnn.valueOf(source.getOpCode().name()), getALHopsForPoolingForwardIM2COL(expr, source, 1, hops)); - setBlockSizeAndRefreshSizeInfo(expr, currBuiltinOp); - break; - } - case AVG_POOL_BACKWARD: - case MAX_POOL_BACKWARD: { - currBuiltinOp = new DnnOp(target.getName(), DataType.MATRIX, target.getValueType(), - OpOpDnn.valueOf(source.getOpCode().name()), getALHopsForConvOpPoolingCOL2IM(expr, source, 1, hops)); - setBlockSizeAndRefreshSizeInfo(expr, currBuiltinOp); - break; - } - case CONV2D: - case CONV2D_BACKWARD_FILTER: - case CONV2D_BACKWARD_DATA: { - currBuiltinOp = new DnnOp(target.getName(), DataType.MATRIX, target.getValueType(), - OpOpDnn.valueOf(source.getOpCode().name()), getALHopsForConvOp(expr, source, 1, hops)); - setBlockSizeAndRefreshSizeInfo(expr, currBuiltinOp); - break; - } - - case ROW_COUNT_DISTINCT: - currBuiltinOp = new AggUnaryOp(target.getName(), DataType.MATRIX, target.getValueType(), + + currBuiltinOp = new DataGenOp(OpOpDG.SAMPLE, target, tmpparams); + break; + } + + case SOLVE: + currBuiltinOp = new BinaryOp(target.getName(), target.getDataType(), target.getValueType(), OpOp2.SOLVE, + expr, expr2); + break; + + case INVERSE: + case CHOLESKY: + case TYPEOF: + case DETECTSCHEMA: + case COLNAMES: + currBuiltinOp = new UnaryOp(target.getName(), target.getDataType(), target.getValueType(), + OpOp1.valueOf(source.getOpCode().name()), expr); + break; + + case OUTER: + if(!(expr3 instanceof LiteralOp)) + throw new HopsException("Operator for outer builtin function must be a constant: " + expr3); + OpOp2 op = OpOp2.valueOfByOpcode(((LiteralOp) expr3).getStringValue()); + if(op == null) + throw new HopsException( + "Unsupported outer vector binary operation: " + ((LiteralOp) expr3).getStringValue()); + + currBuiltinOp = new BinaryOp(target.getName(), DataType.MATRIX, target.getValueType(), op, expr, expr2); + ((BinaryOp) currBuiltinOp).setOuterVectorOperation(true); // flag op as specific outer vector operation + currBuiltinOp.refreshSizeInformation(); // force size reevaluation according to 'outer' flag otherwise + // danger of incorrect dims + break; + + case BIASADD: + case BIASMULT: { + ArrayList inHops1 = new ArrayList<>(); + inHops1.add(expr); + inHops1.add(expr2); + currBuiltinOp = new DnnOp(target.getName(), DataType.MATRIX, target.getValueType(), + OpOpDnn.valueOf(source.getOpCode().name()), inHops1); + setBlockSizeAndRefreshSizeInfo(expr, currBuiltinOp); + break; + } + case AVG_POOL: + case MAX_POOL: { + currBuiltinOp = new DnnOp(target.getName(), DataType.MATRIX, target.getValueType(), + OpOpDnn.valueOf(source.getOpCode().name()), + getALHopsForPoolingForwardIM2COL(expr, source, 1, hops)); + setBlockSizeAndRefreshSizeInfo(expr, currBuiltinOp); + break; + } + case AVG_POOL_BACKWARD: + case MAX_POOL_BACKWARD: { + currBuiltinOp = new DnnOp(target.getName(), DataType.MATRIX, target.getValueType(), + OpOpDnn.valueOf(source.getOpCode().name()), getALHopsForConvOpPoolingCOL2IM(expr, source, 1, hops)); + setBlockSizeAndRefreshSizeInfo(expr, currBuiltinOp); + break; + } + case CONV2D: + case CONV2D_BACKWARD_FILTER: + case CONV2D_BACKWARD_DATA: { + currBuiltinOp = new DnnOp(target.getName(), DataType.MATRIX, target.getValueType(), + OpOpDnn.valueOf(source.getOpCode().name()), getALHopsForConvOp(expr, source, 1, hops)); + setBlockSizeAndRefreshSizeInfo(expr, currBuiltinOp); + break; + } + + case ROW_COUNT_DISTINCT: + currBuiltinOp = new AggUnaryOp(target.getName(), DataType.MATRIX, target.getValueType(), AggOp.valueOf(source.getOpCode().name()), Direction.Row, expr); - break; + break; - case COL_COUNT_DISTINCT: - currBuiltinOp = new AggUnaryOp(target.getName(), DataType.MATRIX, target.getValueType(), + case COL_COUNT_DISTINCT: + currBuiltinOp = new AggUnaryOp(target.getName(), DataType.MATRIX, target.getValueType(), AggOp.valueOf(source.getOpCode().name()), Direction.Col, expr); - break; + break; - default: - throw new ParseException("Unsupported builtin function type: "+source.getOpCode()); + default: + throw new ParseException("Unsupported builtin function type: " + source.getOpCode()); } - boolean isConvolution = source.getOpCode() == Builtins.CONV2D || source.getOpCode() == Builtins.CONV2D_BACKWARD_DATA || - source.getOpCode() == Builtins.CONV2D_BACKWARD_FILTER || - source.getOpCode() == Builtins.MAX_POOL || source.getOpCode() == Builtins.MAX_POOL_BACKWARD || - source.getOpCode() == Builtins.AVG_POOL || source.getOpCode() == Builtins.AVG_POOL_BACKWARD; - if( !isConvolution) { + boolean isConvolution = source.getOpCode() == Builtins.CONV2D || + source.getOpCode() == Builtins.CONV2D_BACKWARD_DATA || + source.getOpCode() == Builtins.CONV2D_BACKWARD_FILTER || source.getOpCode() == Builtins.MAX_POOL || + source.getOpCode() == Builtins.MAX_POOL_BACKWARD || source.getOpCode() == Builtins.AVG_POOL || + source.getOpCode() == Builtins.AVG_POOL_BACKWARD; + if(!isConvolution) { // Since the dimension of output doesnot match that of input variable for these operations setIdentifierParams(currBuiltinOp, source.getOutput()); } @@ -2819,7 +2872,7 @@ else if ( in.length == 2 ) private Hop[] processAllExpressions(Expression[] expr, HashMap hops) { Hop[] ret = new Hop[expr.length]; - for(int i=0; i getALHopsForConvOpPoolingCOL2IM(Hop first, BuiltinFunctionExpression source, int skip, HashMap hops) { + private ArrayList getALHopsForConvOpPoolingCOL2IM(Hop first, BuiltinFunctionExpression source, int skip, + HashMap hops) { ArrayList ret = new ArrayList<>(); ret.add(first); Expression[] allExpr = source.getAllExpr(); for(int i = skip; i < allExpr.length; i++) { if(i == 11) { - ret.add(processExpression(allExpr[7], null, hops)); // Make number of channels of images and filter the same + ret.add(processExpression(allExpr[7], null, hops)); // Make number of channels of images and filter the + // same } else ret.add(processExpression(allExpr[i], null, hops)); @@ -2845,7 +2900,8 @@ private ArrayList getALHopsForConvOpPoolingCOL2IM(Hop first, BuiltinFunctio return ret; } - private ArrayList getALHopsForPoolingForwardIM2COL(Hop first, BuiltinFunctionExpression source, int skip, HashMap hops) { + private ArrayList getALHopsForPoolingForwardIM2COL(Hop first, BuiltinFunctionExpression source, int skip, + HashMap hops) { ArrayList ret = new ArrayList<>(); ret.add(first); Expression[] allExpr = source.getAllExpr(); @@ -2856,7 +2912,7 @@ private ArrayList getALHopsForPoolingForwardIM2COL(Hop first, BuiltinFuncti Expression numChannels = allExpr[6]; for(int i = skip; i < allExpr.length; i++) { - if(i == 10) { + if(i == 10) { ret.add(processExpression(numChannels, null, hops)); } else @@ -2865,8 +2921,9 @@ private ArrayList getALHopsForPoolingForwardIM2COL(Hop first, BuiltinFuncti return ret; } - @SuppressWarnings("unused") //TODO remove if not used - private ArrayList getALHopsForConvOpPoolingIM2COL(Hop first, BuiltinFunctionExpression source, int skip, HashMap hops) { + @SuppressWarnings("unused") // TODO remove if not used + private ArrayList getALHopsForConvOpPoolingIM2COL(Hop first, BuiltinFunctionExpression source, int skip, + HashMap hops) { ArrayList ret = new ArrayList(); ret.add(first); Expression[] allExpr = source.getAllExpr(); @@ -2881,8 +2938,8 @@ else if(skip == 2) { throw new ParseException("Unsupported skip"); } - for (int i = skip; i < allExpr.length; i++) { - if (i == numImgIndex) { // skip=1 ==> i==5 and skip=2 => i==6 + for(int i = skip; i < allExpr.length; i++) { + if(i == numImgIndex) { // skip=1 ==> i==5 and skip=2 => i==6 Expression numImg = allExpr[numImgIndex]; Expression numChannels = allExpr[numImgIndex + 1]; BinaryExpression tmp = new BinaryExpression(org.apache.sysds.parser.Expression.BinaryOp.MULT, numImg); @@ -2891,13 +2948,15 @@ else if(skip == 2) { ret.add(processTempIntExpression(tmp, hops)); ret.add(processExpression(new IntIdentifier(1, numImg), null, hops)); i++; - } else + } + else ret.add(processExpression(allExpr[i], null, hops)); } return ret; } - private ArrayList getALHopsForConvOp(Hop first, BuiltinFunctionExpression source, int skip, HashMap hops) { + private ArrayList getALHopsForConvOp(Hop first, BuiltinFunctionExpression source, int skip, + HashMap hops) { ArrayList ret = new ArrayList<>(); ret.add(first); Expression[] allExpr = source.getAllExpr(); @@ -2908,101 +2967,94 @@ private ArrayList getALHopsForConvOp(Hop first, BuiltinFunctionExpression s } public void setIdentifierParams(Hop h, Identifier id) { - if( id.getDim1()>= 0 ) + if(id.getDim1() >= 0) h.setDim1(id.getDim1()); - if( id.getDim2()>= 0 ) + if(id.getDim2() >= 0) h.setDim2(id.getDim2()); - if( id.getNnz()>= 0 ) + if(id.getNnz() >= 0) h.setNnz(id.getNnz()); h.setBlocksize(id.getBlocksize()); h.setPrivacy(id.getPrivacy()); } - private boolean prepareReadAfterWrite( DMLProgram prog, HashMap pWrites ) { + private boolean prepareReadAfterWrite(DMLProgram prog, HashMap pWrites) { boolean ret = false; - //process functions - /*MB: for the moment we only support read-after-write in the main program - for( FunctionStatementBlock fsb : prog.getFunctionStatementBlocks() ) - ret |= prepareReadAfterWrite(fsb, pWrites); - */ + // process functions + /* + * MB: for the moment we only support read-after-write in the main program for( FunctionStatementBlock fsb : + * prog.getFunctionStatementBlocks() ) ret |= prepareReadAfterWrite(fsb, pWrites); + */ - //process main program - for( StatementBlock sb : prog.getStatementBlocks() ) + // process main program + for(StatementBlock sb : prog.getStatementBlocks()) ret |= prepareReadAfterWrite(sb, pWrites); return ret; } - private boolean prepareReadAfterWrite( StatementBlock sb, HashMap pWrites ) - { + private boolean prepareReadAfterWrite(StatementBlock sb, HashMap pWrites) { boolean ret = false; - if(sb instanceof FunctionStatementBlock) - { + if(sb instanceof FunctionStatementBlock) { FunctionStatementBlock fsb = (FunctionStatementBlock) sb; - FunctionStatement fstmt = (FunctionStatement)fsb.getStatement(0); - for (StatementBlock csb : fstmt.getBody()) + FunctionStatement fstmt = (FunctionStatement) fsb.getStatement(0); + for(StatementBlock csb : fstmt.getBody()) ret |= prepareReadAfterWrite(csb, pWrites); } - else if(sb instanceof WhileStatementBlock) - { + else if(sb instanceof WhileStatementBlock) { WhileStatementBlock wsb = (WhileStatementBlock) sb; - WhileStatement wstmt = (WhileStatement)wsb.getStatement(0); - for (StatementBlock csb : wstmt.getBody()) + WhileStatement wstmt = (WhileStatement) wsb.getStatement(0); + for(StatementBlock csb : wstmt.getBody()) ret |= prepareReadAfterWrite(csb, pWrites); } - else if(sb instanceof IfStatementBlock) - { + else if(sb instanceof IfStatementBlock) { IfStatementBlock isb = (IfStatementBlock) sb; - IfStatement istmt = (IfStatement)isb.getStatement(0); - for (StatementBlock csb : istmt.getIfBody()) + IfStatement istmt = (IfStatement) isb.getStatement(0); + for(StatementBlock csb : istmt.getIfBody()) ret |= prepareReadAfterWrite(csb, pWrites); - for (StatementBlock csb : istmt.getElseBody()) + for(StatementBlock csb : istmt.getElseBody()) ret |= prepareReadAfterWrite(csb, pWrites); } - else if(sb instanceof ForStatementBlock) //incl parfor + else if(sb instanceof ForStatementBlock) // incl parfor { ForStatementBlock fsb = (ForStatementBlock) sb; - ForStatement fstmt = (ForStatement)fsb.getStatement(0); - for (StatementBlock csb : fstmt.getBody()) + ForStatement fstmt = (ForStatement) fsb.getStatement(0); + for(StatementBlock csb : fstmt.getBody()) ret |= prepareReadAfterWrite(csb, pWrites); } - else //generic (last-level) + else // generic (last-level) { - for( Statement s : sb.getStatements() ) - { - //collect persistent write information - if( s instanceof OutputStatement ) - { + for(Statement s : sb.getStatements()) { + // collect persistent write information + if(s instanceof OutputStatement) { OutputStatement os = (OutputStatement) s; String pfname = os.getExprParam(DataExpression.IO_FILENAME).toString(); DataIdentifier di = (DataIdentifier) os.getSource().getOutput(); pWrites.put(pfname, di); } - //propagate size information into reads-after-write - else if( s instanceof AssignmentStatement - && ((AssignmentStatement)s).getSource() instanceof DataExpression ) - { - DataExpression dexpr = (DataExpression) ((AssignmentStatement)s).getSource(); - if (dexpr.isRead()) { + // propagate size information into reads-after-write + else if(s instanceof AssignmentStatement && + ((AssignmentStatement) s).getSource() instanceof DataExpression) { + DataExpression dexpr = (DataExpression) ((AssignmentStatement) s).getSource(); + if(dexpr.isRead()) { String pfname = dexpr.getVarParam(DataExpression.IO_FILENAME).toString(); // found read-after-write - if (pWrites.containsKey(pfname) && !pfname.trim().isEmpty()) { + if(pWrites.containsKey(pfname) && !pfname.trim().isEmpty()) { // update read with essential write meta data DataIdentifier di = pWrites.get(pfname); FileFormat ft = (di.getFileFormat() != null) ? di.getFileFormat() : FileFormat.TEXT; dexpr.addVarParam(DataExpression.FORMAT_TYPE, new StringIdentifier(ft.toString(), di)); - if (di.getDim1() >= 0) + if(di.getDim1() >= 0) dexpr.addVarParam(DataExpression.READROWPARAM, new IntIdentifier(di.getDim1(), di)); - if (di.getDim2() >= 0) + if(di.getDim2() >= 0) dexpr.addVarParam(DataExpression.READCOLPARAM, new IntIdentifier(di.getDim2(), di)); - if (di.getValueType() != ValueType.UNKNOWN) + if(di.getValueType() != ValueType.UNKNOWN) dexpr.addVarParam(DataExpression.VALUETYPEPARAM, - new StringIdentifier(di.getValueType().toExternalString(), di)); - if (di.getDataType() != DataType.UNKNOWN) + new StringIdentifier(di.getValueType().toExternalString(), di)); + if(di.getDataType() != DataType.UNKNOWN) dexpr.addVarParam(DataExpression.DATATYPEPARAM, - new StringIdentifier(di.getDataType().toString(), di)); + new StringIdentifier(di.getDataType().toString(), di)); ret = true; } } diff --git a/src/main/java/org/apache/sysds/runtime/instructions/CPInstructionParser.java b/src/main/java/org/apache/sysds/runtime/instructions/CPInstructionParser.java index cfc4d18b1f9..dc0344d5658 100644 --- a/src/main/java/org/apache/sysds/runtime/instructions/CPInstructionParser.java +++ b/src/main/java/org/apache/sysds/runtime/instructions/CPInstructionParser.java @@ -347,22 +347,22 @@ public class CPInstructionParser extends InstructionParser { } public static CPInstruction parseSingleInstruction(String str) { - if (str == null || str.isEmpty()) + if(str == null || str.isEmpty()) return null; CPType cptype = InstructionUtils.getCPType(str); - if (cptype == null) + if(cptype == null) throw new DMLRuntimeException("Unable derive cptype for instruction: " + str); CPInstruction cpinst = parseSingleInstruction(cptype, str); - if (cpinst == null) + if(cpinst == null) throw new DMLRuntimeException("Unable to parse instruction: " + str); return cpinst; } public static CPInstruction parseSingleInstruction(CPType cptype, String str) { ExecType execType; - if (str == null || str.isEmpty()) + if(str == null || str.isEmpty()) return null; - switch (cptype) { + switch(cptype) { case AggregateUnary: return AggregateUnaryCPInstruction.parseInstruction(str); @@ -437,15 +437,15 @@ public static CPInstruction parseSingleInstruction(CPType cptype, String str) { case MatrixIndexing: execType = ExecType.valueOf(str.split(Instruction.OPERAND_DELIM)[0]); - if (execType == ExecType.CP) + if(execType == ExecType.CP) return IndexingCPInstruction.parseInstruction(str); else // exectype CP_FILE return MatrixIndexingCPFileInstruction.parseInstruction(str); case Builtin: String[] parts = InstructionUtils.getInstructionPartsWithValueType(str); - if (parts[0].equals("log") || parts[0].equals("log_nz")) { - if (InstructionUtils.isInteger(parts[3])) // B=log(A), y=log(x) + if(parts[0].equals("log") || parts[0].equals("log_nz")) { + if(InstructionUtils.isInteger(parts[3])) // B=log(A), y=log(x) // We exploit the fact the number of threads is specified as an integer at parts // 3. return UnaryCPInstruction.parseInstruction(str); diff --git a/src/main/java/org/apache/sysds/runtime/instructions/cp/CPInstruction.java b/src/main/java/org/apache/sysds/runtime/instructions/cp/CPInstruction.java index 1847cc0cae6..8c6c7f730a8 100644 --- a/src/main/java/org/apache/sysds/runtime/instructions/cp/CPInstruction.java +++ b/src/main/java/org/apache/sysds/runtime/instructions/cp/CPInstruction.java @@ -39,13 +39,11 @@ public abstract class CPInstruction extends Instruction { protected static final Log LOG = LogFactory.getLog(CPInstruction.class.getName()); public enum CPType { - AggregateUnary, AggregateBinary, AggregateTernary, - Unary, Binary, Ternary, Quaternary, BuiltinNary, Ctable, + AggregateUnary, AggregateBinary, AggregateTernary, Unary, Binary, Ternary, Quaternary, BuiltinNary, Ctable, MultiReturnParameterizedBuiltin, ParameterizedBuiltin, MultiReturnBuiltin, MultiReturnComplexMatrixBuiltin, - Builtin, Reorg, Variable, FCall, Append, Rand, QSort, QPick, Local, - MatrixIndexing, MMTSJ, PMMJ, MMChain, Reshape, Partition, Compression, DeCompression, SpoofFused, - StringInit, CentralMoment, Covariance, UaggOuterChain, Dnn, Sql, Prefetch, Broadcast, TrigRemote, - NoOp, + Builtin, Reorg, Variable, FCall, Append, Rand, QSort, QPick, Local, MatrixIndexing, MMTSJ, PMMJ, MMChain, + Reshape, Partition, Compression, DeCompression, SpoofFused, StringInit, CentralMoment, Covariance, + UaggOuterChain, Dnn, Sql, Prefetch, Broadcast, TrigRemote, NoOp, } protected final CPType _cptype; @@ -90,18 +88,18 @@ public Instruction preprocessInstruction(ExecutionContext ec) { Instruction tmp = super.preprocessInstruction(ec); // instruction patching - if (tmp.requiresLabelUpdate()) { // update labels only if required + if(tmp.requiresLabelUpdate()) { // update labels only if required // note: no exchange of updated instruction as labels might change in the // general case String updInst = updateLabels(tmp.toString(), ec.getVariables()); tmp = CPInstructionParser.parseSingleInstruction(updInst); // Corrected lineage trace for patched instructions - if (DMLScript.LINEAGE) + if(DMLScript.LINEAGE) ec.traceLineage(tmp); } // robustness federated instructions (runtime assignment) - if (ConfigurationManager.isFederatedRuntimePlanner()) { + if(ConfigurationManager.isFederatedRuntimePlanner()) { tmp = FEDInstructionUtils.checkAndReplaceCP(tmp, ec); // NOTE: Retracing of lineage is not needed as the lineage trace // is same for an instruction and its FED version. @@ -116,28 +114,28 @@ public Instruction preprocessInstruction(ExecutionContext ec) { @Override public void postprocessInstruction(ExecutionContext ec) { - if (DMLScript.LINEAGE_DEBUGGER) + if(DMLScript.LINEAGE_DEBUGGER) ec.maintainLineageDebuggerInfo(this); } /** - * Takes a delimited string of instructions, and replaces ALL placeholder labels - * (such as ##mVar2## and ##Var5##) in ALL instructions. - * + * Takes a delimited string of instructions, and replaces ALL placeholder labels (such as ##mVar2## and ##Var5##) in + * ALL instructions. + * * @param instList instruction list as string * @param labelValueMapping local variable map * @return instruction list after replacement */ public static String updateLabels(String instList, LocalVariableMap labelValueMapping) { - if (!instList.contains(Lop.VARIABLE_NAME_PLACEHOLDER)) + if(!instList.contains(Lop.VARIABLE_NAME_PLACEHOLDER)) return instList; StringBuilder updateInstList = new StringBuilder(); String[] ilist = instList.split(Lop.INSTRUCTION_DELIMITOR); - for (int i = 0; i < ilist.length; i++) { - if (i > 0) + for(int i = 0; i < ilist.length; i++) { + if(i > 0) updateInstList.append(Lop.INSTRUCTION_DELIMITOR); updateInstList.append(updateInstLabels(ilist[i], labelValueMapping)); @@ -146,33 +144,31 @@ public static String updateLabels(String instList, LocalVariableMap labelValueMa } /** - * Replaces ALL placeholder strings (such as ##mVar2## and ##Var5##) in a single - * instruction. - * + * Replaces ALL placeholder strings (such as ##mVar2## and ##Var5##) in a single instruction. + * * @param inst string instruction * @param map local variable map * @return string instruction after replacement */ private static String updateInstLabels(String inst, LocalVariableMap map) { - if (inst.contains(Lop.VARIABLE_NAME_PLACEHOLDER)) { + if(inst.contains(Lop.VARIABLE_NAME_PLACEHOLDER)) { int skip = Lop.VARIABLE_NAME_PLACEHOLDER.length(); - while (inst.contains(Lop.VARIABLE_NAME_PLACEHOLDER)) { + while(inst.contains(Lop.VARIABLE_NAME_PLACEHOLDER)) { int startLoc = inst.indexOf(Lop.VARIABLE_NAME_PLACEHOLDER) + skip; String varName = inst.substring(startLoc, inst.indexOf(Lop.VARIABLE_NAME_PLACEHOLDER, startLoc)); String replacement = getVarNameReplacement(inst, varName, map); inst = inst.replaceAll(Lop.VARIABLE_NAME_PLACEHOLDER + varName + Lop.VARIABLE_NAME_PLACEHOLDER, - replacement); + replacement); } } return inst; } /** - * Computes the replacement string for a given variable name placeholder string - * (e.g., ##mVar2## or ##Var5##). The replacement is a HDFS filename for matrix - * variables, and is the actual value (stored in symbol table) for scalar + * Computes the replacement string for a given variable name placeholder string (e.g., ##mVar2## or ##Var5##). The + * replacement is a HDFS filename for matrix variables, and is the actual value (stored in symbol table) for scalar * variables. - * + * * @param inst instruction * @param varName variable name * @param map local variable map @@ -180,18 +176,19 @@ private static String updateInstLabels(String inst, LocalVariableMap map) { */ private static String getVarNameReplacement(String inst, String varName, LocalVariableMap map) { Data val = map.get(varName); - if (val != null) { + if(val != null) { String replacement = null; - if (val.getDataType() == DataType.MATRIX) { + if(val.getDataType() == DataType.MATRIX) { replacement = ((MatrixObject) val).getFileName(); } - if (val.getDataType() == DataType.SCALAR) + if(val.getDataType() == DataType.SCALAR) replacement = "" + ((ScalarObject) val).getStringValue(); return replacement; - } else { + } + else { throw new DMLRuntimeException( - "Variable (" + varName + ") in Instruction (" + inst + ") is not found in the variablemap."); + "Variable (" + varName + ") in Instruction (" + inst + ") is not found in the variablemap."); } } } diff --git a/src/main/java/org/apache/sysds/runtime/instructions/cp/MultiReturnBuiltinCPInstruction.java b/src/main/java/org/apache/sysds/runtime/instructions/cp/MultiReturnBuiltinCPInstruction.java index 26874cc8708..051a6928c99 100644 --- a/src/main/java/org/apache/sysds/runtime/instructions/cp/MultiReturnBuiltinCPInstruction.java +++ b/src/main/java/org/apache/sysds/runtime/instructions/cp/MultiReturnBuiltinCPInstruction.java @@ -39,7 +39,7 @@ public class MultiReturnBuiltinCPInstruction extends ComputationCPInstruction { protected ArrayList _outputs; private MultiReturnBuiltinCPInstruction(Operator op, CPOperand input1, ArrayList outputs, String opcode, - String istr) { + String istr) { super(CPType.MultiReturnBuiltin, op, input1, null, outputs.get(0), opcode, istr); _outputs = outputs; } @@ -62,14 +62,15 @@ public static MultiReturnBuiltinCPInstruction parseInstruction(String str) { // first part is always the opcode String opcode = parts[0]; - if (opcode.equalsIgnoreCase("qr")) { + if(opcode.equalsIgnoreCase("qr")) { // one input and two ouputs CPOperand in1 = new CPOperand(parts[1]); outputs.add(new CPOperand(parts[2], ValueType.FP64, DataType.MATRIX)); outputs.add(new CPOperand(parts[3], ValueType.FP64, DataType.MATRIX)); return new MultiReturnBuiltinCPInstruction(null, in1, outputs, opcode, str); - } else if (opcode.equalsIgnoreCase("lu")) { + } + else if(opcode.equalsIgnoreCase("lu")) { CPOperand in1 = new CPOperand(parts[1]); // one input and three outputs @@ -79,7 +80,8 @@ public static MultiReturnBuiltinCPInstruction parseInstruction(String str) { return new MultiReturnBuiltinCPInstruction(null, in1, outputs, opcode, str); - } else if (opcode.equalsIgnoreCase("eigen")) { + } + else if(opcode.equalsIgnoreCase("eigen")) { // one input and two outputs CPOperand in1 = new CPOperand(parts[1]); outputs.add(new CPOperand(parts[2], ValueType.FP64, DataType.MATRIX)); @@ -87,7 +89,8 @@ public static MultiReturnBuiltinCPInstruction parseInstruction(String str) { return new MultiReturnBuiltinCPInstruction(null, in1, outputs, opcode, str); - } else if (opcode.equalsIgnoreCase("fft")) { + } + else if(opcode.equalsIgnoreCase("fft")) { // one input and two outputs CPOperand in1 = new CPOperand(parts[1]); outputs.add(new CPOperand(parts[2], ValueType.FP64, DataType.MATRIX)); @@ -95,7 +98,8 @@ public static MultiReturnBuiltinCPInstruction parseInstruction(String str) { return new MultiReturnBuiltinCPInstruction(null, in1, outputs, opcode, str); - } else if (opcode.equalsIgnoreCase("svd")) { + } + else if(opcode.equalsIgnoreCase("svd")) { CPOperand in1 = new CPOperand(parts[1]); // one input and three outputs @@ -105,7 +109,8 @@ public static MultiReturnBuiltinCPInstruction parseInstruction(String str) { return new MultiReturnBuiltinCPInstruction(null, in1, outputs, opcode, str); - } else { + } + else { throw new DMLRuntimeException("Invalid opcode in MultiReturnBuiltin instruction: " + opcode); } @@ -117,13 +122,13 @@ public int getNumOutputs() { @Override public void processInstruction(ExecutionContext ec) { - if (!LibCommonsMath.isSupportedMultiReturnOperation(getOpcode())) + if(!LibCommonsMath.isSupportedMultiReturnOperation(getOpcode())) throw new DMLRuntimeException("Invalid opcode in MultiReturnBuiltin instruction: " + getOpcode()); MatrixBlock in = ec.getMatrixInput(input1.getName()); MatrixBlock[] out = LibCommonsMath.multiReturnOperations(in, getOpcode()); ec.releaseMatrixInput(input1.getName()); - for (int i = 0; i < _outputs.size(); i++) { + for(int i = 0; i < _outputs.size(); i++) { ec.setMatrixOutput(_outputs.get(i).getName(), out[i]); } } @@ -138,7 +143,7 @@ public boolean hasSingleLineage() { public Pair[] getLineageItems(ExecutionContext ec) { LineageItem[] inputLineage = LineageItemUtils.getLineage(ec, input1, input2, input3); final Pair[] ret = new Pair[_outputs.size()]; - for (int i = 0; i < _outputs.size(); i++) { + for(int i = 0; i < _outputs.size(); i++) { CPOperand out = _outputs.get(i); ret[i] = Pair.of(out.getName(), new LineageItem(getOpcode(), inputLineage)); } diff --git a/src/main/java/org/apache/sysds/runtime/instructions/cp/MultiReturnComplexMatrixBuiltinCPInstruction.java b/src/main/java/org/apache/sysds/runtime/instructions/cp/MultiReturnComplexMatrixBuiltinCPInstruction.java index ae045dbc3e5..66f9f307455 100644 --- a/src/main/java/org/apache/sysds/runtime/instructions/cp/MultiReturnComplexMatrixBuiltinCPInstruction.java +++ b/src/main/java/org/apache/sysds/runtime/instructions/cp/MultiReturnComplexMatrixBuiltinCPInstruction.java @@ -36,117 +36,116 @@ public class MultiReturnComplexMatrixBuiltinCPInstruction extends ComputationCPInstruction { - protected ArrayList _outputs; - - private MultiReturnComplexMatrixBuiltinCPInstruction(Operator op, CPOperand input1, CPOperand input2, - ArrayList outputs, String opcode, - String istr) { - super(CPType.MultiReturnBuiltin, op, input1, input2, outputs.get(0), opcode, istr); - _outputs = outputs; - } - - private MultiReturnComplexMatrixBuiltinCPInstruction(Operator op, CPOperand input1, ArrayList outputs, - String opcode, - String istr) { - super(CPType.MultiReturnBuiltin, op, input1, null, outputs.get(0), opcode, istr); - _outputs = outputs; - } - - public CPOperand getOutput(int i) { - return _outputs.get(i); - } - - public List getOutputs() { - return _outputs; - } - - public String[] getOutputNames() { - return _outputs.parallelStream().map(output -> output.getName()).toArray(String[]::new); - } - - public static MultiReturnComplexMatrixBuiltinCPInstruction parseInstruction(String str) { - String[] parts = InstructionUtils.getInstructionPartsWithValueType(str); - ArrayList outputs = new ArrayList<>(); - // first part is always the opcode - String opcode = parts[0]; - - if (parts.length == 5 && opcode.equalsIgnoreCase("ifft")) { - // one input and two outputs - CPOperand in1 = new CPOperand(parts[1]); - CPOperand in2 = new CPOperand(parts[2]); - outputs.add(new CPOperand(parts[3], ValueType.FP64, DataType.MATRIX)); - outputs.add(new CPOperand(parts[4], ValueType.FP64, DataType.MATRIX)); - - return new MultiReturnComplexMatrixBuiltinCPInstruction(null, in1, in2, outputs, opcode, str); - } else if (parts.length == 4 && opcode.equalsIgnoreCase("ifft")) { - // one input and two outputs - CPOperand in1 = new CPOperand(parts[1]); - outputs.add(new CPOperand(parts[2], ValueType.FP64, DataType.MATRIX)); - outputs.add(new CPOperand(parts[3], ValueType.FP64, DataType.MATRIX)); - - return new MultiReturnComplexMatrixBuiltinCPInstruction(null, in1, outputs, opcode, str); - } - - { - throw new DMLRuntimeException("Invalid opcode in MultiReturnBuiltin instruction: " + opcode); - } - - } - - public int getNumOutputs() { - return _outputs.size(); - } - - @Override - public void processInstruction(ExecutionContext ec) { - if (input2 == null) - processOneInputInstruction(ec); - else - processTwoInputInstruction(ec); - } - - private void processOneInputInstruction(ExecutionContext ec) { - if (!LibCommonsMath.isSupportedMultiReturnOperation(getOpcode())) - throw new DMLRuntimeException("Invalid opcode in MultiReturnBuiltin instruction: " + getOpcode()); - - MatrixBlock in = ec.getMatrixInput(input1.getName()); - MatrixBlock[] out = LibCommonsMath.multiReturnOperations(in, getOpcode()); - - ec.releaseMatrixInput(input1.getName()); - - for (int i = 0; i < _outputs.size(); i++) { - ec.setMatrixOutput(_outputs.get(i).getName(), out[i]); - } - } - - private void processTwoInputInstruction(ExecutionContext ec) { - if (!LibCommonsMath.isSupportedMultiReturnOperation(getOpcode())) - throw new DMLRuntimeException("Invalid opcode in MultiReturnBuiltin instruction: " + getOpcode()); - - MatrixBlock in1 = ec.getMatrixInput(input1.getName()); - MatrixBlock in2 = ec.getMatrixInput(input2.getName()); - MatrixBlock[] out = LibCommonsMath.multiReturnOperations(in1, in2, getOpcode()); - ec.releaseMatrixInput(input1.getName(), input2.getName()); - - for (int i = 0; i < _outputs.size(); i++) { - ec.setMatrixOutput(_outputs.get(i).getName(), out[i]); - } - } - - @Override - public boolean hasSingleLineage() { - return false; - } - - @Override - @SuppressWarnings("unchecked") - public Pair[] getLineageItems(ExecutionContext ec) { - LineageItem[] inputLineage = LineageItemUtils.getLineage(ec, input1, input2, input3); - final Pair[] ret = new Pair[_outputs.size()]; - for (int i = 0; i < _outputs.size(); i++) { - CPOperand out = _outputs.get(i); - ret[i] = Pair.of(out.getName(), new LineageItem(getOpcode(), inputLineage)); - } - return ret; - } + protected ArrayList _outputs; + + private MultiReturnComplexMatrixBuiltinCPInstruction(Operator op, CPOperand input1, CPOperand input2, + ArrayList outputs, String opcode, String istr) { + super(CPType.MultiReturnBuiltin, op, input1, input2, outputs.get(0), opcode, istr); + _outputs = outputs; + } + + private MultiReturnComplexMatrixBuiltinCPInstruction(Operator op, CPOperand input1, ArrayList outputs, + String opcode, String istr) { + super(CPType.MultiReturnBuiltin, op, input1, null, outputs.get(0), opcode, istr); + _outputs = outputs; + } + + public CPOperand getOutput(int i) { + return _outputs.get(i); + } + + public List getOutputs() { + return _outputs; + } + + public String[] getOutputNames() { + return _outputs.parallelStream().map(output -> output.getName()).toArray(String[]::new); + } + + public static MultiReturnComplexMatrixBuiltinCPInstruction parseInstruction(String str) { + String[] parts = InstructionUtils.getInstructionPartsWithValueType(str); + ArrayList outputs = new ArrayList<>(); + // first part is always the opcode + String opcode = parts[0]; + + if(parts.length == 5 && opcode.equalsIgnoreCase("ifft")) { + // one input and two outputs + CPOperand in1 = new CPOperand(parts[1]); + CPOperand in2 = new CPOperand(parts[2]); + outputs.add(new CPOperand(parts[3], ValueType.FP64, DataType.MATRIX)); + outputs.add(new CPOperand(parts[4], ValueType.FP64, DataType.MATRIX)); + + return new MultiReturnComplexMatrixBuiltinCPInstruction(null, in1, in2, outputs, opcode, str); + } + else if(parts.length == 4 && opcode.equalsIgnoreCase("ifft")) { + // one input and two outputs + CPOperand in1 = new CPOperand(parts[1]); + outputs.add(new CPOperand(parts[2], ValueType.FP64, DataType.MATRIX)); + outputs.add(new CPOperand(parts[3], ValueType.FP64, DataType.MATRIX)); + + return new MultiReturnComplexMatrixBuiltinCPInstruction(null, in1, outputs, opcode, str); + } + + { + throw new DMLRuntimeException("Invalid opcode in MultiReturnBuiltin instruction: " + opcode); + } + + } + + public int getNumOutputs() { + return _outputs.size(); + } + + @Override + public void processInstruction(ExecutionContext ec) { + if(input2 == null) + processOneInputInstruction(ec); + else + processTwoInputInstruction(ec); + } + + private void processOneInputInstruction(ExecutionContext ec) { + if(!LibCommonsMath.isSupportedMultiReturnOperation(getOpcode())) + throw new DMLRuntimeException("Invalid opcode in MultiReturnBuiltin instruction: " + getOpcode()); + + MatrixBlock in = ec.getMatrixInput(input1.getName()); + MatrixBlock[] out = LibCommonsMath.multiReturnOperations(in, getOpcode()); + + ec.releaseMatrixInput(input1.getName()); + + for(int i = 0; i < _outputs.size(); i++) { + ec.setMatrixOutput(_outputs.get(i).getName(), out[i]); + } + } + + private void processTwoInputInstruction(ExecutionContext ec) { + if(!LibCommonsMath.isSupportedMultiReturnOperation(getOpcode())) + throw new DMLRuntimeException("Invalid opcode in MultiReturnBuiltin instruction: " + getOpcode()); + + MatrixBlock in1 = ec.getMatrixInput(input1.getName()); + MatrixBlock in2 = ec.getMatrixInput(input2.getName()); + MatrixBlock[] out = LibCommonsMath.multiReturnOperations(in1, in2, getOpcode()); + ec.releaseMatrixInput(input1.getName(), input2.getName()); + + for(int i = 0; i < _outputs.size(); i++) { + ec.setMatrixOutput(_outputs.get(i).getName(), out[i]); + } + } + + @Override + public boolean hasSingleLineage() { + return false; + } + + @Override + @SuppressWarnings("unchecked") + public Pair[] getLineageItems(ExecutionContext ec) { + LineageItem[] inputLineage = LineageItemUtils.getLineage(ec, input1, input2, input3); + final Pair[] ret = new Pair[_outputs.size()]; + for(int i = 0; i < _outputs.size(); i++) { + CPOperand out = _outputs.get(i); + ret[i] = Pair.of(out.getName(), new LineageItem(getOpcode(), inputLineage)); + } + return ret; + } } diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java index 924477100d7..2c3e514f9cf 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java @@ -53,11 +53,9 @@ import static org.apache.sysds.runtime.matrix.data.LibMatrixFourier.ifft; /** - * Library for matrix operations that need invocation of - * Apache Commons Math library. + * Library for matrix operations that need invocation of Apache Commons Math library. * - * This library currently supports following operations: - * matrix inverse, matrix decompositions (QR, LU, Eigen), solve + * This library currently supports following operations: matrix inverse, matrix decompositions (QR, LU, Eigen), solve */ public class LibCommonsMath { private static final Log LOG = LogFactory.getLog(LibCommonsMath.class.getName()); @@ -69,12 +67,12 @@ private LibCommonsMath() { } public static boolean isSupportedUnaryOperation(String opcode) { - return (opcode.equals("inverse") || opcode.equals("cholesky")); + return(opcode.equals("inverse") || opcode.equals("cholesky")); } public static boolean isSupportedMultiReturnOperation(String opcode) { - switch (opcode) { + switch(opcode) { case "qr": case "lu": case "eigen": @@ -89,14 +87,14 @@ public static boolean isSupportedMultiReturnOperation(String opcode) { } public static boolean isSupportedMatrixMatrixOperation(String opcode) { - return (opcode.equals("solve")); + return(opcode.equals("solve")); } public static MatrixBlock unaryOperations(MatrixBlock inj, String opcode) { Array2DRowRealMatrix matrixInput = DataConverter.convertToArray2DRowRealMatrix(inj); - if (opcode.equals("inverse")) + if(opcode.equals("inverse")) return computeMatrixInverse(matrixInput); - else if (opcode.equals("cholesky")) + else if(opcode.equals("cholesky")) return computeCholesky(matrixInput); return null; } @@ -110,8 +108,8 @@ public static MatrixBlock[] multiReturnOperations(MatrixBlock in1, MatrixBlock i } public static MatrixBlock[] multiReturnOperations(MatrixBlock in, String opcode, int threads, int num_iterations, - double tol) { - if (opcode.equals("eigen_qr")) + double tol) { + if(opcode.equals("eigen_qr")) return computeEigenQR(in, num_iterations, tol, threads); else return multiReturnOperations(in, opcode, threads, 1); @@ -119,7 +117,7 @@ public static MatrixBlock[] multiReturnOperations(MatrixBlock in, String opcode, public static MatrixBlock[] multiReturnOperations(MatrixBlock in, String opcode, int threads, long seed) { - switch (opcode) { + switch(opcode) { case "qr": return computeQR(in); case "qr2": @@ -145,9 +143,9 @@ public static MatrixBlock[] multiReturnOperations(MatrixBlock in, String opcode, } public static MatrixBlock[] multiReturnOperations(MatrixBlock in1, MatrixBlock in2, String opcode, int threads, - long seed) { + long seed) { - switch (opcode) { + switch(opcode) { case "ifft": return computeIFFT(in1, in2); default: @@ -157,8 +155,8 @@ public static MatrixBlock[] multiReturnOperations(MatrixBlock in1, MatrixBlock i } public static MatrixBlock matrixMatrixOperations(MatrixBlock in1, MatrixBlock in2, String opcode) { - if (opcode.equals("solve")) { - if (in1.getNumRows() != in1.getNumColumns()) + if(opcode.equals("solve")) { + if(in1.getNumRows() != in1.getNumColumns()) throw new DMLRuntimeException("The A matrix, in solve(A,b) should have squared dimensions."); return computeSolve(in1, in2); } @@ -180,9 +178,8 @@ private static MatrixBlock computeSolve(MatrixBlock in1, MatrixBlock in2) { BlockRealMatrix vectorInput = DataConverter.convertToBlockRealMatrix(in2); /* - * LUDecompositionImpl ludecompose = new LUDecompositionImpl(matrixInput); - * DecompositionSolver lusolver = ludecompose.getSolver(); - * RealMatrix solutionMatrix = lusolver.solve(vectorInput); + * LUDecompositionImpl ludecompose = new LUDecompositionImpl(matrixInput); DecompositionSolver lusolver = + * ludecompose.getSolver(); RealMatrix solutionMatrix = lusolver.solve(vectorInput); */ // Setup a solver based on QR Decomposition @@ -212,7 +209,7 @@ private static MatrixBlock[] computeQR(MatrixBlock in) { MatrixBlock mbH = DataConverter.convertToMatrixBlock(H.getData()); MatrixBlock mbR = DataConverter.convertToMatrixBlock(R.getData()); - return new MatrixBlock[] { mbH, mbR }; + return new MatrixBlock[] {mbH, mbR}; } /** @@ -222,10 +219,10 @@ private static MatrixBlock[] computeQR(MatrixBlock in) { * @return array of matrix blocks */ private static MatrixBlock[] computeLU(MatrixBlock in) { - if (in.getNumRows() != in.getNumColumns()) { + if(in.getNumRows() != in.getNumColumns()) { throw new DMLRuntimeException( - "LU Decomposition can only be done on a square matrix. Input matrix is rectangular (rows=" - + in.getNumRows() + ", cols=" + in.getNumColumns() + ")"); + "LU Decomposition can only be done on a square matrix. Input matrix is rectangular (rows=" + + in.getNumRows() + ", cols=" + in.getNumColumns() + ")"); } Array2DRowRealMatrix matrixInput = DataConverter.convertToArray2DRowRealMatrix(in); @@ -241,27 +238,27 @@ private static MatrixBlock[] computeLU(MatrixBlock in) { MatrixBlock mbL = DataConverter.convertToMatrixBlock(L.getData()); MatrixBlock mbU = DataConverter.convertToMatrixBlock(U.getData()); - return new MatrixBlock[] { mbP, mbL, mbU }; + return new MatrixBlock[] {mbP, mbL, mbU}; } /** - * Function to perform Eigen decomposition on a given matrix. - * Input must be a symmetric matrix. + * Function to perform Eigen decomposition on a given matrix. Input must be a symmetric matrix. * * @param in matrix object * @return array of matrix blocks */ private static MatrixBlock[] computeEigen(MatrixBlock in) { - if (in.getNumRows() != in.getNumColumns()) { + if(in.getNumRows() != in.getNumColumns()) { throw new DMLRuntimeException("Eigen Decomposition can only be done on a square matrix. " - + "Input matrix is rectangular (rows=" + in.getNumRows() + ", cols=" + in.getNumColumns() + ")"); + + "Input matrix is rectangular (rows=" + in.getNumRows() + ", cols=" + in.getNumColumns() + ")"); } EigenDecomposition eigendecompose = null; try { Array2DRowRealMatrix matrixInput = DataConverter.convertToArray2DRowRealMatrix(in); eigendecompose = new EigenDecomposition(matrixInput); - } catch (MaxCountExceededException ex) { + } + catch(MaxCountExceededException ex) { LOG.warn("Eigen: " + ex.getMessage() + ". Falling back to regularized eigen factorization."); eigendecompose = computeEigenRegularized(in); } @@ -274,70 +271,68 @@ private static MatrixBlock[] computeEigen(MatrixBlock in) { } private static EigenDecomposition computeEigenRegularized(MatrixBlock in) { - if (in == null || in.isEmptyBlock(false)) + if(in == null || in.isEmptyBlock(false)) throw new DMLRuntimeException("Invalid empty block"); // slightly modify input for regularization (pos/neg) MatrixBlock in2 = new MatrixBlock(in, false); DenseBlock a = in2.getDenseBlock(); - for (int i = 0; i < in2.rlen; i++) { + for(int i = 0; i < in2.rlen; i++) { double[] avals = a.values(i); int apos = a.pos(i); - for (int j = 0; j < in2.clen; j++) { + for(int j = 0; j < in2.clen; j++) { double v = avals[apos + j]; avals[apos + j] += Math.signum(v) * EIGEN_LAMBDA; } } // run eigen decomposition - return new EigenDecomposition( - DataConverter.convertToArray2DRowRealMatrix(in2)); + return new EigenDecomposition(DataConverter.convertToArray2DRowRealMatrix(in2)); } /** * Function to perform FFT on a given matrix. * - * @param in matrix object + * @param re matrix object for real values * @return array of matrix blocks */ - private static MatrixBlock[] computeFFT(MatrixBlock in) { - if (in == null || in.isEmptyBlock(false)) + private static MatrixBlock[] computeFFT(MatrixBlock re) { + if(re == null || re.isEmptyBlock(false)) throw new DMLRuntimeException("Invalid empty block"); // run fft - in.sparseToDense(); - return fft(in); + re.sparseToDense(); + return fft(re); } /** * Function to perform IFFT on a given matrix. * - * @param in matrix object + * @param re matrix object for the real part of the values + * @param im matrix object for the imaginary part of the values * @return array of matrix blocks */ - private static MatrixBlock[] computeIFFT(MatrixBlock in1, MatrixBlock in2) { - if (in1 == null || in1.isEmptyBlock(false)) + private static MatrixBlock[] computeIFFT(MatrixBlock re, MatrixBlock im) { + if(re == null || re.isEmptyBlock(false)) throw new DMLRuntimeException("Invalid empty block"); // run ifft - if (in2 != null) { - in1.sparseToDense(); - in2.sparseToDense(); - return ifft(in1, in2); - } else { - in1.sparseToDense(); - return ifft(in1); + if(im != null) { + re.sparseToDense(); + im.sparseToDense(); + return ifft(re, im); + } + else { + re.sparseToDense(); + return ifft(re); } } /** - * Performs Singular Value Decomposition. Calls Apache Commons Math SVD. - * X = U * Sigma * Vt, where X is the input matrix, - * U is the left singular matrix, Sigma is the singular values matrix returned - * as a - * column matrix and Vt is the transpose of the right singular matrix V. - * However, the returned array has { U, Sigma, V} + * Performs Singular Value Decomposition. Calls Apache Commons Math SVD. X = U * Sigma * Vt, where X is the input + * matrix, U is the left singular matrix, Sigma is the singular values matrix returned as a column matrix and Vt is + * the transpose of the right singular matrix V. However, the returned array has { U, Sigma, V} * * @param in Input matrix * @return An array containing U, Sigma & V @@ -354,7 +349,7 @@ private static MatrixBlock[] computeSvd(MatrixBlock in) { Sigma = LibMatrixReorg.diag(Sigma, new MatrixBlock(Sigma.rlen, Sigma.rlen, true)); MatrixBlock V = DataConverter.convertToMatrixBlock(v.getData()); - return new MatrixBlock[] { U, Sigma, V }; + return new MatrixBlock[] {U, Sigma, V}; } /** @@ -364,9 +359,9 @@ private static MatrixBlock[] computeSvd(MatrixBlock in) { * @return matrix block */ private static MatrixBlock computeMatrixInverse(Array2DRowRealMatrix in) { - if (!in.isSquare()) + if(!in.isSquare()) throw new DMLRuntimeException("Input to inv() must be square matrix -- given: a " + in.getRowDimension() - + "x" + in.getColumnDimension() + " matrix."); + + "x" + in.getColumnDimension() + " matrix."); QRDecomposition qrdecompose = new QRDecomposition(in); DecompositionSolver solver = qrdecompose.getSolver(); @@ -376,18 +371,18 @@ private static MatrixBlock computeMatrixInverse(Array2DRowRealMatrix in) { } /** - * Function to compute Cholesky decomposition of the given input matrix. - * The input must be a real symmetric positive-definite matrix. + * Function to compute Cholesky decomposition of the given input matrix. The input must be a real symmetric + * positive-definite matrix. * * @param in commons-math3 Array2DRowRealMatrix * @return matrix block */ private static MatrixBlock computeCholesky(Array2DRowRealMatrix in) { - if (!in.isSquare()) + if(!in.isSquare()) throw new DMLRuntimeException("Input to cholesky() must be square matrix -- given: a " - + in.getRowDimension() + "x" + in.getColumnDimension() + " matrix."); + + in.getRowDimension() + "x" + in.getColumnDimension() + " matrix."); CholeskyDecomposition cholesky = new CholeskyDecomposition(in, RELATIVE_SYMMETRY_THRESHOLD, - CholeskyDecomposition.DEFAULT_ABSOLUTE_POSITIVITY_THRESHOLD); + CholeskyDecomposition.DEFAULT_ABSOLUTE_POSITIVITY_THRESHOLD); RealMatrix rmL = cholesky.getL(); return DataConverter.convertToMatrixBlock(rmL.getData()); } @@ -409,18 +404,16 @@ private static MatrixBlock randNormalizedVect(int dim, int threads, long seed) { UnaryOperator op_sqrt = new UnaryOperator(Builtin.getBuiltinFnObject(Builtin.BuiltinCode.SQRT), threads, true); v1 = v1.unaryOperations(op_sqrt, new MatrixBlock()); - if (Math.abs(v1.sumSq() - 1.0) >= 1e-7) + if(Math.abs(v1.sumSq() - 1.0) >= 1e-7) throw new DMLRuntimeException("v1 not correctly normalized (maybe try changing the seed)"); return v1; } /** - * Function to perform the Lanczos algorithm and then computes the - * Eigendecomposition. - * Caution: Lanczos is not numerically stable (see - * https://en.wikipedia.org/wiki/Lanczos_algorithm) - * Input must be a symmetric (and square) matrix. + * Function to perform the Lanczos algorithm and then computes the Eigendecomposition. Caution: Lanczos is not + * numerically stable (see https://en.wikipedia.org/wiki/Lanczos_algorithm) Input must be a symmetric (and square) + * matrix. * * @param in matrix object * @param threads number of threads @@ -428,11 +421,10 @@ private static MatrixBlock randNormalizedVect(int dim, int threads, long seed) { * @return array of matrix blocks */ private static MatrixBlock[] computeEigenLanczos(MatrixBlock in, int threads, long seed) { - if (in.getNumRows() != in.getNumColumns()) { + if(in.getNumRows() != in.getNumColumns()) { throw new DMLRuntimeException( - "Lanczos algorithm and Eigen Decomposition can only be done on a square matrix. " - + "Input matrix is rectangular (rows=" + in.getNumRows() + ", cols=" + in.getNumColumns() - + ")"); + "Lanczos algorithm and Eigen Decomposition can only be done on a square matrix. " + + "Input matrix is rectangular (rows=" + in.getNumRows() + ", cols=" + in.getNumColumns() + ")"); } int m = in.getNumRows(); @@ -449,12 +441,12 @@ private static MatrixBlock[] computeEigenLanczos(MatrixBlock in, int threads, lo ScalarOperator op_div_scalar = new RightScalarOperator(Divide.getDivideFnObject(), 1, threads); MatrixBlock beta = new MatrixBlock(1, 1, 0.0); - for (int i = 0; i < m; i++) { + for(int i = 0; i < m; i++) { v1.putInto(TV, 0, i, false); w1 = in.aggregateBinaryOperations(in, v1, op_mul_agg); MatrixBlock alpha = w1.aggregateBinaryOperations(v1.reorgOperations(op_t, new MatrixBlock(), 0, 0, m), w1, - op_mul_agg); - if (i < m - 1) { + op_mul_agg); + if(i < m - 1) { w1 = w1.ternaryOperations(op_minus_mul, v1, alpha, new MatrixBlock()); w1 = w1.ternaryOperations(op_minus_mul, v0, beta, new MatrixBlock()); beta.setValue(0, 0, Math.sqrt(w1.sumSq())); @@ -475,19 +467,17 @@ private static MatrixBlock[] computeEigenLanczos(MatrixBlock in, int threads, lo } /** - * Function to perform the QR decomposition. - * Input must be a square matrix. - * TODO: use Householder transformation and implicit shifts to further speed up - * QR decompositions + * Function to perform the QR decomposition. Input must be a square matrix. TODO: use Householder transformation and + * implicit shifts to further speed up QR decompositions * * @param in matrix object * @param threads number of threads * @return array of matrix blocks [Q, R] */ private static MatrixBlock[] computeQR2(MatrixBlock in, int threads) { - if (in.getNumRows() != in.getNumColumns()) { + if(in.getNumRows() != in.getNumColumns()) { throw new DMLRuntimeException("QR2 Decomposition can only be done on a square matrix. " - + "Input matrix is rectangular (rows=" + in.getNumRows() + ", cols=" + in.getNumColumns() + ")"); + + "Input matrix is rectangular (rows=" + in.getNumRows() + ", cols=" + in.getNumColumns() + ")"); } int m = in.rlen; @@ -496,7 +486,7 @@ private static MatrixBlock[] computeQR2(MatrixBlock in, int threads) { A_n.copy(in); MatrixBlock Q_n = new MatrixBlock(m, m, true); - for (int i = 0; i < m; i++) { + for(int i = 0; i < m; i++) { Q_n.setValue(i, i, 1.0); } @@ -506,7 +496,7 @@ private static MatrixBlock[] computeQR2(MatrixBlock in, int threads) { ScalarOperator op_div_scalar = new RightScalarOperator(Divide.getDivideFnObject(), 1, threads); ScalarOperator op_mult_2 = new LeftScalarOperator(Multiply.getMultiplyFnObject(), 2, threads); - for (int k = 0; k < m; k++) { + for(int k = 0; k < m; k++) { MatrixBlock z = A_n.slice(k, m - 1, k, k); MatrixBlock uk = new MatrixBlock(m - k, 1, 0.0); uk.copy(z); @@ -525,13 +515,12 @@ private static MatrixBlock[] computeQR2(MatrixBlock in, int threads) { Q_n = Q_n.binaryOperations(op_sub, Q_n.aggregateBinaryOperations(Q_n, vkvkt2, op_mul_agg)); } // QR decomp: Q: Q_n; R: A_n - return new MatrixBlock[] { Q_n, A_n }; + return new MatrixBlock[] {Q_n, A_n}; } /** - * Function that computes the Eigen Decomposition using the QR algorithm. - * Caution: check if the QR algorithm is converged, if not increase iterations - * Caution: if the input matrix has complex eigenvalues results will be + * Function that computes the Eigen Decomposition using the QR algorithm. Caution: check if the QR algorithm is + * converged, if not increase iterations Caution: if the input matrix has complex eigenvalues results will be * incorrect * * @param in Input matrix @@ -543,20 +532,20 @@ private static MatrixBlock[] computeEigenQR(MatrixBlock in, int threads) { } private static MatrixBlock[] computeEigenQR(MatrixBlock in, int num_iterations, double tol, int threads) { - if (in.getNumRows() != in.getNumColumns()) { + if(in.getNumRows() != in.getNumColumns()) { throw new DMLRuntimeException("Eigen Decomposition (QR) can only be done on a square matrix. " - + "Input matrix is rectangular (rows=" + in.getNumRows() + ", cols=" + in.getNumColumns() + ")"); + + "Input matrix is rectangular (rows=" + in.getNumRows() + ", cols=" + in.getNumColumns() + ")"); } int m = in.rlen; AggregateBinaryOperator op_mul_agg = InstructionUtils.getMatMultOperator(threads); MatrixBlock Q_prod = new MatrixBlock(m, m, 0.0); - for (int i = 0; i < m; i++) { + for(int i = 0; i < m; i++) { Q_prod.setValue(i, i, 1.0); } - for (int i = 0; i < num_iterations; i++) { + for(int i = 0; i < num_iterations; i++) { MatrixBlock[] QR = computeQR2(in, threads); Q_prod = Q_prod.aggregateBinaryOperations(Q_prod, QR[0], op_mul_agg); in = QR[1].aggregateBinaryOperations(QR[1], QR[0], op_mul_agg); @@ -567,7 +556,7 @@ private static MatrixBlock[] computeEigenQR(MatrixBlock in, int num_iterations, double[] check = in.getDenseBlockValues(); double[] eval = new double[m]; - for (int i = 0; i < m; i++) + for(int i = 0; i < m; i++) eval[i] = check[i * m + i]; double[] evec = Q_prod.getDenseBlockValues(); @@ -588,14 +577,14 @@ private static MatrixBlock computeHouseholder(MatrixBlock in, int threads) { MatrixBlock A_n = new MatrixBlock(m, m, 0.0); A_n.copy(in); - for (int k = 0; k < m - 2; k++) { + for(int k = 0; k < m - 2; k++) { MatrixBlock ajk = A_n.slice(0, m - 1, k, k); - for (int i = 0; i <= k; i++) { + for(int i = 0; i <= k; i++) { ajk.setValue(i, 0, 0.0); } double alpha = Math.sqrt(ajk.sumSq()); double ak1k = A_n.getDouble(k + 1, k); - if (ak1k > 0.0) + if(ak1k > 0.0) alpha *= -1; double r = Math.sqrt(0.5 * (alpha * alpha - ak1k * alpha)); MatrixBlock v = new MatrixBlock(m, 1, 0.0); @@ -605,7 +594,7 @@ private static MatrixBlock computeHouseholder(MatrixBlock in, int threads) { v = v.scalarOperations(op_div_scalar, new MatrixBlock()); MatrixBlock P = new MatrixBlock(m, m, 0.0); - for (int i = 0; i < m; i++) { + for(int i = 0; i < m; i++) { P.setValue(i, i, 1.0); } @@ -624,8 +613,7 @@ private static MatrixBlock computeHouseholder(MatrixBlock in, int threads) { } /** - * Sort the eigen values (and vectors) in increasing order (to be compatible w/ - * LAPACK.DSYEVR()) + * Sort the eigen values (and vectors) in increasing order (to be compatible w/ LAPACK.DSYEVR()) * * @param eValues Eigenvalues * @param eVectors Eigenvectors @@ -633,19 +621,19 @@ private static MatrixBlock computeHouseholder(MatrixBlock in, int threads) { */ private static MatrixBlock[] sortEVs(double[] eValues, double[][] eVectors) { int n = eValues.length; - for (int i = 0; i < n; i++) { + for(int i = 0; i < n; i++) { int k = i; double p = eValues[i]; - for (int j = i + 1; j < n; j++) { - if (eValues[j] < p) { + for(int j = i + 1; j < n; j++) { + if(eValues[j] < p) { k = j; p = eValues[j]; } } - if (k != i) { + if(k != i) { eValues[k] = eValues[i]; eValues[i] = p; - for (int j = 0; j < n; j++) { + for(int j = 0; j < n; j++) { p = eVectors[j][i]; eVectors[j][i] = eVectors[j][k]; eVectors[j][k] = p; @@ -655,24 +643,24 @@ private static MatrixBlock[] sortEVs(double[] eValues, double[][] eVectors) { MatrixBlock eval = DataConverter.convertToMatrixBlock(eValues, true); MatrixBlock evec = DataConverter.convertToMatrixBlock(eVectors); - return new MatrixBlock[] { eval, evec }; + return new MatrixBlock[] {eval, evec}; } private static MatrixBlock[] sortEVs(double[] eValues, double[] eVectors) { int n = eValues.length; - for (int i = 0; i < n; i++) { + for(int i = 0; i < n; i++) { int k = i; double p = eValues[i]; - for (int j = i + 1; j < n; j++) { - if (eValues[j] < p) { + for(int j = i + 1; j < n; j++) { + if(eValues[j] < p) { k = j; p = eValues[j]; } } - if (k != i) { + if(k != i) { eValues[k] = eValues[i]; eValues[i] = p; - for (int j = 0; j < n; j++) { + for(int j = 0; j < n; j++) { p = eVectors[j * n + i]; eVectors[j * n + i] = eVectors[j * n + k]; eVectors[j * n + k] = p; @@ -683,6 +671,6 @@ private static MatrixBlock[] sortEVs(double[] eValues, double[] eVectors) { MatrixBlock eval = DataConverter.convertToMatrixBlock(eValues, true); MatrixBlock evec = new MatrixBlock(n, n, false); evec.init(eVectors, n, n); - return new MatrixBlock[] { eval, evec }; + return new MatrixBlock[] {eval, evec}; } } diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java index 2c9fd8339dd..b53f7b68f5b 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java @@ -24,118 +24,119 @@ public class LibMatrixFourier { /** - * Function to perform FFT on two given matrices. - * The first one represents the real values and the second one the imaginary values. - * The output also contains one matrix for the real and one for the imaginary values. + * Function to perform FFT on two given matrices. The first one represents the real values and the second one the + * imaginary values. The output also contains one matrix for the real and one for the imaginary values. * * @param re matrix object representing the real values * @param im matrix object representing the imaginary values * @return array of two matrix blocks */ - public static MatrixBlock[] fft(MatrixBlock re, MatrixBlock im){ + public static MatrixBlock[] fft(MatrixBlock re, MatrixBlock im) { int rows = re.getNumRows(); int cols = re.getNumColumns(); - if(!isPowerOfTwo(rows) || !isPowerOfTwo(cols)) throw new RuntimeException("false dimensions"); + if(!isPowerOfTwo(rows) || !isPowerOfTwo(cols)) + throw new RuntimeException("false dimensions"); fft(re.getDenseBlockValues(), im.getDenseBlockValues(), rows, cols); - return new MatrixBlock[]{re, im}; + return new MatrixBlock[] {re, im}; } /** - * Function to perform IFFT on two given matrices. - * The first one represents the real values and the second one the imaginary values. - * The output also contains one matrix for the real and one for the imaginary values. + * Function to perform IFFT on two given matrices. The first one represents the real values and the second one the + * imaginary values. The output also contains one matrix for the real and one for the imaginary values. * * @param re matrix object representing the real values * @param im matrix object representing the imaginary values * @return array of two matrix blocks */ - public static MatrixBlock[] ifft(MatrixBlock re, MatrixBlock im){ + public static MatrixBlock[] ifft(MatrixBlock re, MatrixBlock im) { int rows = re.getNumRows(); int cols = re.getNumColumns(); - if(!isPowerOfTwo(rows) || !isPowerOfTwo(cols)) throw new RuntimeException("false dimensions"); + if(!isPowerOfTwo(rows) || !isPowerOfTwo(cols)) + throw new RuntimeException("false dimensions"); ifft(re.getDenseBlockValues(), im.getDenseBlockValues(), rows, cols); - return new MatrixBlock[]{re, im}; + return new MatrixBlock[] {re, im}; } /** - * Function to perform FFT on two given double arrays. - * The first one represents the real values and the second one the imaginary values. - * Both arrays get updated and contain the result. + * Function to perform FFT on two given double arrays. The first one represents the real values and the second one + * the imaginary values. Both arrays get updated and contain the result. * - * @param re array representing the real values - * @param im array representing the imaginary values + * @param re array representing the real values + * @param im array representing the imaginary values * @param rows number of rows * @param cols number of rows */ public static void fft(double[] re, double[] im, int rows, int cols) { - double[] re_inter = new double[rows*cols]; - double[] im_inter = new double[rows*cols]; + double[] re_inter = new double[rows * cols]; + double[] im_inter = new double[rows * cols]; - for(int i = 0; i < rows; i++){ - fft_one_dim(re, im, re_inter, im_inter, i*cols, (i+1)*cols, cols, 1); + for(int i = 0; i < rows; i++) { + fft_one_dim(re, im, re_inter, im_inter, i * cols, (i + 1) * cols, cols, 1); } - for(int j = 0; j < cols; j++){ - fft_one_dim(re, im, re_inter, im_inter, j, j+rows*cols, rows, cols); + for(int j = 0; j < cols; j++) { + fft_one_dim(re, im, re_inter, im_inter, j, j + rows * cols, rows, cols); } } /** - * Function to perform IFFT on two given double arrays. - * The first one represents the real values and the second one the imaginary values. - * Both arrays get updated and contain the result. + * Function to perform IFFT on two given double arrays. The first one represents the real values and the second one + * the imaginary values. Both arrays get updated and contain the result. * - * @param re array representing the real values - * @param im array representing the imaginary values + * @param re array representing the real values + * @param im array representing the imaginary values * @param rows number of rows * @param cols number of rows */ public static void ifft(double[] re, double[] im, int rows, int cols) { - double[] re_inter = new double[rows*cols]; - double[] im_inter = new double[rows*cols]; + double[] re_inter = new double[rows * cols]; + double[] im_inter = new double[rows * cols]; - for(int j = 0; j < cols; j++){ - ifft_one_dim(re, im, re_inter, im_inter, j, j+rows*cols, rows, cols); + for(int j = 0; j < cols; j++) { + ifft_one_dim(re, im, re_inter, im_inter, j, j + rows * cols, rows, cols); } - for(int i = 0; i < rows; i++){ - ifft_one_dim(re, im, re_inter, im_inter, i*cols, (i+1)*cols, cols, 1); + for(int i = 0; i < rows; i++) { + ifft_one_dim(re, im, re_inter, im_inter, i * cols, (i + 1) * cols, cols, 1); } } - public static void fft_one_dim(double[] re, double[] im, double[] re_inter, double[] im_inter, int start, int stop, int num, int minStep) { + public static void fft_one_dim(double[] re, double[] im, double[] re_inter, double[] im_inter, int start, int stop, + int num, int minStep) { // start inclusive, stop exclusive - if(num == 1) return; + if(num == 1) + return; // even indices - for(int step = minStep*(num/2), subNum = 2; subNum <= num; step/=2, subNum*=2){ + for(int step = minStep * (num / 2), subNum = 2; subNum <= num; step /= 2, subNum *= 2) { - double angle = -2*FastMath.PI/subNum; + double angle = -2 * FastMath.PI / subNum; // use ceil for the main (sub)array - for(int sub = 0; sub < FastMath.ceil(num/(2*(double)subNum)); sub++){ + for(int sub = 0; sub < FastMath.ceil(num / (2 * (double) subNum)); sub++) { for(int isOdd = 0; isOdd < 2; isOdd++) { // no odd values for main (sub)array - if (isOdd == 1 && subNum == num) return; + if(isOdd == 1 && subNum == num) + return; - int startSub = start + sub*minStep + isOdd*(step/2); + int startSub = start + sub * minStep + isOdd * (step / 2); // first iterates over even indices, then over odd indices - for (int j = startSub, cnt = 0; cnt < subNum / 2; j += 2*step, cnt++) { + for(int j = startSub, cnt = 0; cnt < subNum / 2; j += 2 * step, cnt++) { double omega_pow_re = FastMath.cos(cnt * angle); double omega_pow_im = FastMath.sin(cnt * angle); @@ -146,13 +147,13 @@ public static void fft_one_dim(double[] re, double[] im, double[] re_inter, doub int index = startSub + cnt * step; re_inter[index] = re[j] + m_re; - re_inter[index + (stop-start)/2] = re[j] - m_re; + re_inter[index + (stop - start) / 2] = re[j] - m_re; im_inter[index] = im[j] + m_im; - im_inter[index + (stop-start)/2] = im[j] - m_im; + im_inter[index + (stop - start) / 2] = im[j] - m_im; } - for (int j = startSub; j < startSub + (stop-start); j += step) { + for(int j = startSub; j < startSub + (stop - start); j += step) { re[j] = re_inter[j]; im[j] = im_inter[j]; re_inter[j] = 0; @@ -167,21 +168,22 @@ public static void fft_one_dim(double[] re, double[] im, double[] re_inter, doub } - private static void ifft_one_dim(double[] re, double[] im, double[] re_inter, double[] im_inter, int start, int stop, int num, int minStep) { + private static void ifft_one_dim(double[] re, double[] im, double[] re_inter, double[] im_inter, int start, + int stop, int num, int minStep) { // conjugate input - for (int i = start; i < start+num*minStep; i+=minStep){ + for(int i = start; i < start + num * minStep; i += minStep) { im[i] = -im[i]; } // apply fft - //fft_one_dim_recursive(re, im, re_inter, im_inter, start, step, num, subArraySize); + // fft_one_dim_recursive(re, im, re_inter, im_inter, start, step, num, subArraySize); fft_one_dim(re, im, re_inter, im_inter, start, stop, num, minStep); // conjugate and scale result - for (int i = start; i < start+num*minStep; i+=minStep){ - re[i] = re[i]/num; - im[i] = -im[i]/num; + for(int i = start; i < start + num * minStep; i += minStep) { + re[i] = re[i] / num; + im[i] = -im[i] / num; } } @@ -192,32 +194,32 @@ private static void ifft_one_dim(double[] re, double[] im, double[] re_inter, do * @param n integer to check * @return true if n is a power of two, false otherwise */ - public static boolean isPowerOfTwo(int n){ + public static boolean isPowerOfTwo(int n) { return (n != 0) && ((n & (n - 1)) == 0); } /** - * Function to perform FFT on a given matrices. - * The given matrix only represents real values. - * The output contains one matrix for the real and one for the imaginary values. + * Function to perform FFT on a given matrices. The given matrix only represents real values. The output contains + * one matrix for the real and one for the imaginary values. * * @param re matrix object representing the real values * @return array of two matrix blocks */ - public static MatrixBlock[] fft(MatrixBlock re){ - return fft(re, new MatrixBlock(re.getNumRows(),re.getNumColumns(), new double[re.getNumRows()*re.getNumColumns()])); + public static MatrixBlock[] fft(MatrixBlock re) { + return fft(re, + new MatrixBlock(re.getNumRows(), re.getNumColumns(), new double[re.getNumRows() * re.getNumColumns()])); } /** - * Function to perform IFFT on a given matrices. - * The given matrix only represents real values. - * The output contains one matrix for the real and one for the imaginary values. + * Function to perform IFFT on a given matrices. The given matrix only represents real values. The output contains + * one matrix for the real and one for the imaginary values. * * @param re matrix object representing the real values * @return array of two matrix blocks */ - public static MatrixBlock[] ifft(MatrixBlock re){ - return ifft(re, new MatrixBlock(re.getNumRows(),re.getNumColumns(), new double[re.getNumRows()*re.getNumColumns()])); + public static MatrixBlock[] ifft(MatrixBlock re) { + return ifft(re, + new MatrixBlock(re.getNumRows(), re.getNumColumns(), new double[re.getNumRows() * re.getNumColumns()])); } } diff --git a/src/main/java/org/apache/sysds/runtime/privacy/propagation/PrivacyPropagator.java b/src/main/java/org/apache/sysds/runtime/privacy/propagation/PrivacyPropagator.java index 4d2996b7856..089d290582d 100644 --- a/src/main/java/org/apache/sysds/runtime/privacy/propagation/PrivacyPropagator.java +++ b/src/main/java/org/apache/sysds/runtime/privacy/propagation/PrivacyPropagator.java @@ -70,40 +70,38 @@ import org.apache.wink.json4j.JSONObject; /** - * Class with static methods merging privacy constraints of operands - * in expressions to generate the privacy constraints of the output. + * Class with static methods merging privacy constraints of operands in expressions to generate the privacy constraints + * of the output. */ public class PrivacyPropagator { /** - * Parses the privacy constraint of the given metadata object - * and sets the field of the given Data if the privacy constraint is not null. + * Parses the privacy constraint of the given metadata object and sets the field of the given Data if the privacy + * constraint is not null. * * @param cd data for which privacy constraint is set * @param mtd metadata object * @return data object with privacy constraint set * @throws JSONException during parsing of metadata */ - public static Data parseAndSetPrivacyConstraint(Data cd, JSONObject mtd) - throws JSONException { + public static Data parseAndSetPrivacyConstraint(Data cd, JSONObject mtd) throws JSONException { PrivacyConstraint mtdPrivConstraint = parseAndReturnPrivacyConstraint(mtd); - if (mtdPrivConstraint != null) + if(mtdPrivConstraint != null) cd.setPrivacyConstraints(mtdPrivConstraint); return cd; } /** - * Parses the privacy constraint of the given metadata object - * or returns null if no privacy constraint is set in the metadata. + * Parses the privacy constraint of the given metadata object or returns null if no privacy constraint is set in the + * metadata. * * @param mtd metadata * @return privacy constraint parsed from metadata object * @throws JSONException during parsing of metadata */ - public static PrivacyConstraint parseAndReturnPrivacyConstraint(JSONObject mtd) - throws JSONException { - if (mtd.containsKey(DataExpression.PRIVACY)) { + public static PrivacyConstraint parseAndReturnPrivacyConstraint(JSONObject mtd) throws JSONException { + if(mtd.containsKey(DataExpression.PRIVACY)) { String privacyLevel = mtd.getString(DataExpression.PRIVACY); - if (privacyLevel != null) + if(privacyLevel != null) return new PrivacyConstraint(PrivacyLevel.valueOf(privacyLevel)); } return null; @@ -114,84 +112,67 @@ private static boolean anyInputHasLevel(PrivacyLevel[] inputLevels, PrivacyLevel } /** - * Returns the output privacy level based on the given input privacy levels and - * operator type. - * It represents the basic logic of privacy propagation: + * Returns the output privacy level based on the given input privacy levels and operator type. It represents the + * basic logic of privacy propagation: * - * Unary input: - * Input | NonAggregate | Aggregate - * ----------------------------------- - * priv | priv | priv - * privAgg | privAgg | none - * none | none | none + * Unary input: Input | NonAggregate | Aggregate ----------------------------------- priv | priv | priv privAgg | + * privAgg | none none | none | none * - * Binary input: - * Input | NonAggregate | Aggregate - * -------------------------------------------- - * priv-priv | priv | priv - * priv-privAgg | priv | priv - * priv-none | priv | priv - * privAgg-priv | priv | priv - * none-priv | priv | priv - * privAgg-privAgg | privAgg | none - * none-none | none | none - * privAgg-none | privAgg | none - * none-privAgg | privAgg | none + * Binary input: Input | NonAggregate | Aggregate -------------------------------------------- priv-priv | priv | + * priv priv-privAgg | priv | priv priv-none | priv | priv privAgg-priv | priv | priv none-priv | priv | priv + * privAgg-privAgg | privAgg | none none-none | none | none privAgg-none | privAgg | none none-privAgg | privAgg | + * none * * @param inputLevels privacy levels of the input - * @param operatorType type of the operator which is either an aggregation - * (Aggregate) or not an aggregation (NonAggregate) + * @param operatorType type of the operator which is either an aggregation (Aggregate) or not an aggregation + * (NonAggregate) * @return output privacy level */ public static PrivacyLevel corePropagation(PrivacyLevel[] inputLevels, OperatorType operatorType) { - if (anyInputHasLevel(inputLevels, PrivacyLevel.Private)) + if(anyInputHasLevel(inputLevels, PrivacyLevel.Private)) return PrivacyLevel.Private; - if (operatorType == OperatorType.Aggregate) + if(operatorType == OperatorType.Aggregate) return PrivacyLevel.None; - if (operatorType == OperatorType.NonAggregate && anyInputHasLevel(inputLevels, PrivacyLevel.PrivateAggregation)) + if(operatorType == OperatorType.NonAggregate && anyInputHasLevel(inputLevels, PrivacyLevel.PrivateAggregation)) return PrivacyLevel.PrivateAggregation; return PrivacyLevel.None; } /** - * Merges the given privacy constraints with the core propagation using the - * given operator type. + * Merges the given privacy constraints with the core propagation using the given operator type. * * @param privacyConstraints array of privacy constraints to merge - * @param operatorType type of operation to use when merging with the core - * propagation + * @param operatorType type of operation to use when merging with the core propagation * @return merged privacy constraint */ private static PrivacyConstraint mergeNary(PrivacyConstraint[] privacyConstraints, OperatorType operatorType) { - PrivacyLevel[] privacyLevels = Arrays.stream(privacyConstraints) - .map(constraint -> { - if (constraint != null) - return constraint.getPrivacyLevel(); - else - return PrivacyLevel.None; - }) - .toArray(PrivacyLevel[]::new); + PrivacyLevel[] privacyLevels = Arrays.stream(privacyConstraints).map(constraint -> { + if(constraint != null) + return constraint.getPrivacyLevel(); + else + return PrivacyLevel.None; + }).toArray(PrivacyLevel[]::new); PrivacyLevel outputPrivacyLevel = corePropagation(privacyLevels, operatorType); return new PrivacyConstraint(outputPrivacyLevel); } /** - * Merges the input privacy constraints using the core propagation with - * NonAggregate operator type. + * Merges the input privacy constraints using the core propagation with NonAggregate operator type. * * @param privacyConstraint1 first privacy constraint * @param privacyConstraint2 second privacy constraint * @return merged privacy constraint */ public static PrivacyConstraint mergeBinary(PrivacyConstraint privacyConstraint1, - PrivacyConstraint privacyConstraint2) { - if (privacyConstraint1 != null && privacyConstraint2 != null) { - PrivacyLevel[] privacyLevels = new PrivacyLevel[] { - privacyConstraint1.getPrivacyLevel(), privacyConstraint2.getPrivacyLevel() }; + PrivacyConstraint privacyConstraint2) { + if(privacyConstraint1 != null && privacyConstraint2 != null) { + PrivacyLevel[] privacyLevels = new PrivacyLevel[] {privacyConstraint1.getPrivacyLevel(), + privacyConstraint2.getPrivacyLevel()}; return new PrivacyConstraint(corePropagation(privacyLevels, OperatorType.NonAggregate)); - } else if (privacyConstraint1 != null) + } + else if(privacyConstraint1 != null) return privacyConstraint1; - else if (privacyConstraint2 != null) + else if(privacyConstraint2 != null) return privacyConstraint2; return null; } @@ -212,37 +193,35 @@ public static void hopPropagation(Hop hop) { * @param inputHops inputs to given hop */ public static void hopPropagation(Hop hop, ArrayList inputHops) { - PrivacyConstraint[] inputConstraints = inputHops.stream() - .map(Hop::getPrivacy).toArray(PrivacyConstraint[]::new); + PrivacyConstraint[] inputConstraints = inputHops.stream().map(Hop::getPrivacy) + .toArray(PrivacyConstraint[]::new); OperatorType opType = getOpType(hop); hop.setPrivacy(mergeNary(inputConstraints, opType)); - if (opType == null && Arrays.stream(inputConstraints).anyMatch(Objects::nonNull)) - throw new DMLException("Input has constraint but hop type not recognized by PrivacyPropagator. " + - "Hop is " + hop + " " + hop.getClass()); + if(opType == null && Arrays.stream(inputConstraints).anyMatch(Objects::nonNull)) + throw new DMLException("Input has constraint but hop type not recognized by PrivacyPropagator. " + "Hop is " + + hop + " " + hop.getClass()); } /** - * Get operator type of given hop. - * Returns null if hop type is not known. + * Get operator type of given hop. Returns null if hop type is not known. * * @param hop for which operator type is returned * @return operator type of hop or null if hop type is unknown */ private static OperatorType getOpType(Hop hop) { - if (hop instanceof TernaryOp || hop instanceof BinaryOp || hop instanceof ReorgOp - || hop instanceof DataOp || hop instanceof LiteralOp || hop instanceof NaryOp - || hop instanceof DataGenOp || hop instanceof FunctionOp || hop instanceof IndexingOp - || hop instanceof ParameterizedBuiltinOp || hop instanceof LeftIndexingOp) + if(hop instanceof TernaryOp || hop instanceof BinaryOp || hop instanceof ReorgOp || hop instanceof DataOp || + hop instanceof LiteralOp || hop instanceof NaryOp || hop instanceof DataGenOp || + hop instanceof FunctionOp || hop instanceof IndexingOp || hop instanceof ParameterizedBuiltinOp || + hop instanceof LeftIndexingOp) return OperatorType.NonAggregate; - else if (hop instanceof AggBinaryOp || hop instanceof AggUnaryOp || hop instanceof UnaryOp) + else if(hop instanceof AggBinaryOp || hop instanceof AggUnaryOp || hop instanceof UnaryOp) return OperatorType.Aggregate; else return null; } /** - * Propagate privacy constraints to output variables - * based on privacy constraint of CPOperand output in instruction + * Propagate privacy constraints to output variables based on privacy constraint of CPOperand output in instruction * which has been set during privacy propagation preprocessing. * * @param inst instruction for which privacy constraints are propagated @@ -251,26 +230,25 @@ else if (hop instanceof AggBinaryOp || hop instanceof AggUnaryOp || hop instance public static void postProcessInstruction(Instruction inst, ExecutionContext ec) { // if inst has output List instOutputs = getOutputOperands(inst); - if (!instOutputs.isEmpty()) { - for (CPOperand output : instOutputs) { + if(!instOutputs.isEmpty()) { + for(CPOperand output : instOutputs) { PrivacyConstraint outputPrivacyConstraint = output.getPrivacyConstraint(); - if (PrivacyUtils.someConstraintSetUnary(outputPrivacyConstraint)) + if(PrivacyUtils.someConstraintSetUnary(outputPrivacyConstraint)) setOutputPrivacyConstraint(ec, outputPrivacyConstraint, output.getName()); } } } /** - * Propagate privacy constraints from input to output CPOperands - * in case the privacy constraints of the input are activated. + * Propagate privacy constraints from input to output CPOperands in case the privacy constraints of the input are + * activated. * * @param inst instruction for which the privacy constraints are propagated * @param ec execution context - * @return instruction with propagated privacy constraints (usually the same - * instance as the input inst) + * @return instruction with propagated privacy constraints (usually the same instance as the input inst) */ public static Instruction preprocessInstruction(Instruction inst, ExecutionContext ec) { - switch (inst.getType()) { + switch(inst.getType()) { case CONTROL_PROGRAM: return preprocessCPInstruction((CPInstruction) inst, ec); case BREAKPOINT: @@ -284,7 +262,7 @@ public static Instruction preprocessInstruction(Instruction inst, ExecutionConte } private static Instruction preprocessCPInstruction(CPInstruction inst, ExecutionContext ec) { - switch (inst.getCPInstructionType()) { + switch(inst.getCPInstructionType()) { case Binary: case Builtin: case BuiltinNary: @@ -305,7 +283,7 @@ private static Instruction preprocessCPInstruction(CPInstruction inst, Execution case Append: return preprocessAppendCPInstruction((AppendCPInstruction) inst, ec); case AggregateBinary: - if (inst instanceof AggregateBinaryCPInstruction) + if(inst instanceof AggregateBinaryCPInstruction) return preprocessAggregateBinaryCPInstruction((AggregateBinaryCPInstruction) inst, ec); else return throwExceptionIfInputOrInstPrivacy(inst, ec); @@ -323,7 +301,7 @@ private static Instruction preprocessCPInstruction(CPInstruction inst, Execution } private static Instruction preprocessVariableCPInstruction(VariableCPInstruction inst, ExecutionContext ec) { - switch (inst.getVariableOpcode()) { + switch(inst.getVariableOpcode()) { case CopyVariable: case MoveVariable: case RemoveVariableAndFile: @@ -351,28 +329,28 @@ private static Instruction preprocessVariableCPInstruction(VariableCPInstruction } /** - * Propagates fine-grained constraints if input has fine-grained constraints, - * otherwise it propagates general constraints. + * Propagates fine-grained constraints if input has fine-grained constraints, otherwise it propagates general + * constraints. * * @param inst aggregate binary instruction for which constraints are propagated * @param ec execution context - * @return instruction with merged privacy constraints propagated to it and - * output CPOperand + * @return instruction with merged privacy constraints propagated to it and output CPOperand */ private static Instruction preprocessAggregateBinaryCPInstruction(AggregateBinaryCPInstruction inst, - ExecutionContext ec) { + ExecutionContext ec) { PrivacyConstraint[] privacyConstraints = getInputPrivacyConstraints(ec, inst.getInputs()); - if (PrivacyUtils.someConstraintSetBinary(privacyConstraints)) { + if(PrivacyUtils.someConstraintSetBinary(privacyConstraints)) { PrivacyConstraint mergedPrivacyConstraint; - if ((privacyConstraints[0] != null && privacyConstraints[0].hasFineGrainedConstraints()) || - (privacyConstraints[1] != null && privacyConstraints[1].hasFineGrainedConstraints())) { + if((privacyConstraints[0] != null && privacyConstraints[0].hasFineGrainedConstraints()) || + (privacyConstraints[1] != null && privacyConstraints[1].hasFineGrainedConstraints())) { MatrixBlock input1 = ec.getMatrixInput(inst.input1.getName()); MatrixBlock input2 = ec.getMatrixInput(inst.input2.getName()); Propagator propagator = new MatrixMultiplicationPropagatorPrivateFirst(input1, privacyConstraints[0], - input2, privacyConstraints[1]); + input2, privacyConstraints[1]); mergedPrivacyConstraint = propagator.propagate(); ec.releaseMatrixInput(inst.input1.getName(), inst.input2.getName()); - } else { + } + else { mergedPrivacyConstraint = mergeNary(privacyConstraints, OperatorType.getAggregationType(inst, ec)); inst.setPrivacyConstraint(mergedPrivacyConstraint); } @@ -382,50 +360,51 @@ private static Instruction preprocessAggregateBinaryCPInstruction(AggregateBinar } /** - * Propagates input privacy constraints using general and fine-grained - * constraints depending on the AppendType. + * Propagates input privacy constraints using general and fine-grained constraints depending on the AppendType. * * @param inst append instruction for which constraints are propagated * @param ec execution context - * @return instruction with merged privacy constraints propagated to it and - * output CPOperand + * @return instruction with merged privacy constraints propagated to it and output CPOperand */ private static Instruction preprocessAppendCPInstruction(AppendCPInstruction inst, ExecutionContext ec) { PrivacyConstraint[] privacyConstraints = getInputPrivacyConstraints(ec, inst.getInputs()); - if (PrivacyUtils.someConstraintSetBinary(privacyConstraints)) { - if (inst.getAppendType() == AppendCPInstruction.AppendType.STRING) { + if(PrivacyUtils.someConstraintSetBinary(privacyConstraints)) { + if(inst.getAppendType() == AppendCPInstruction.AppendType.STRING) { PrivacyLevel[] privacyLevels = new PrivacyLevel[2]; privacyLevels[0] = PrivacyUtils.getGeneralPrivacyLevel(privacyConstraints[0]); privacyLevels[1] = PrivacyUtils.getGeneralPrivacyLevel(privacyConstraints[1]); PrivacyConstraint outputConstraint = new PrivacyConstraint( - corePropagation(privacyLevels, OperatorType.NonAggregate)); + corePropagation(privacyLevels, OperatorType.NonAggregate)); inst.output.setPrivacyConstraint(outputConstraint); - } else if (inst.getAppendType() == AppendCPInstruction.AppendType.LIST) { + } + else if(inst.getAppendType() == AppendCPInstruction.AppendType.LIST) { ListObject input1 = (ListObject) ec.getVariable(inst.input1); - if (inst.getOpcode().equals("remove")) { + if(inst.getOpcode().equals("remove")) { ScalarObject removePosition = ec.getScalarInput(inst.input2); PropagatorMultiReturn propagator = new ListRemovePropagator(input1, privacyConstraints[0], - removePosition, removePosition.getPrivacyConstraint()); + removePosition, removePosition.getPrivacyConstraint()); PrivacyConstraint[] outputConstraints = propagator.propagate(); inst.output.setPrivacyConstraint(outputConstraints[0]); ((ListAppendRemoveCPInstruction) inst).getOutput2().setPrivacyConstraint(outputConstraints[1]); - } else { + } + else { ListObject input2 = (ListObject) ec.getVariable(inst.input2); Propagator propagator = new ListAppendPropagator(input1, privacyConstraints[0], input2, - privacyConstraints[1]); + privacyConstraints[1]); inst.output.setPrivacyConstraint(propagator.propagate()); } - } else { + } + else { MatrixBlock input1 = ec.getMatrixInput(inst.input1.getName()); MatrixBlock input2 = ec.getMatrixInput(inst.input2.getName()); Propagator propagator; - if (inst.getAppendType() == AppendCPInstruction.AppendType.RBIND) + if(inst.getAppendType() == AppendCPInstruction.AppendType.RBIND) propagator = new RBindPropagator(input1, privacyConstraints[0], input2, privacyConstraints[1]); - else if (inst.getAppendType() == AppendCPInstruction.AppendType.CBIND) + else if(inst.getAppendType() == AppendCPInstruction.AppendType.CBIND) propagator = new CBindPropagator(input1, privacyConstraints[0], input2, privacyConstraints[1]); else - throw new DMLPrivacyException("Instruction " + inst.getCPInstructionType() + " with append type " + - inst.getAppendType() + " is not supported by the privacy propagator"); + throw new DMLPrivacyException("Instruction " + inst.getCPInstructionType() + " with append type " + + inst.getAppendType() + " is not supported by the privacy propagator"); inst.output.setPrivacyConstraint(propagator.propagate()); ec.releaseMatrixInput(inst.input1.getName(), inst.input2.getName()); } @@ -434,44 +413,40 @@ else if (inst.getAppendType() == AppendCPInstruction.AppendType.CBIND) } /** - * Propagates privacy constraints from input to instruction and output CPOperand - * based on given operator type. - * The propagation is done through the core propagation. + * Propagates privacy constraints from input to instruction and output CPOperand based on given operator type. The + * propagation is done through the core propagation. * * @param inst instruction for which privacy is propagated * @param ec execution context * @param operatorType defining whether the instruction is aggregating the input - * @return instruction with the merged privacy constraint propagated to it and - * output CPOperand + * @return instruction with the merged privacy constraint propagated to it and output CPOperand */ private static Instruction mergePrivacyConstraintsFromInput(Instruction inst, ExecutionContext ec, - OperatorType operatorType) { + OperatorType operatorType) { return mergePrivacyConstraintsFromInput(inst, ec, getInputOperands(inst), getOutputOperands(inst), - operatorType); + operatorType); } /** - * Propagates privacy constraints from input to instruction and output CPOperand - * based on given operator type. - * The propagation is done through the core propagation. + * Propagates privacy constraints from input to instruction and output CPOperand based on given operator type. The + * propagation is done through the core propagation. * * @param inst instruction for which privacy is propagated * @param ec execution context * @param inputs to instruction * @param outputs of instruction * @param operatorType defining whether the instruction is aggregating the input - * @return instruction with the merged privacy constraint propagated to it and - * output CPOperand + * @return instruction with the merged privacy constraint propagated to it and output CPOperand */ private static Instruction mergePrivacyConstraintsFromInput(Instruction inst, ExecutionContext ec, - CPOperand[] inputs, List outputs, OperatorType operatorType) { - if (inputs != null && inputs.length > 0) { + CPOperand[] inputs, List outputs, OperatorType operatorType) { + if(inputs != null && inputs.length > 0) { PrivacyConstraint[] privacyConstraints = getInputPrivacyConstraints(ec, inputs); - if (privacyConstraints != null) { + if(privacyConstraints != null) { PrivacyConstraint mergedPrivacyConstraint = mergeNary(privacyConstraints, operatorType); inst.setPrivacyConstraint(mergedPrivacyConstraint); - for (CPOperand output : outputs) { - if (output != null) { + for(CPOperand output : outputs) { + if(output != null) { output.setPrivacyConstraint(mergedPrivacyConstraint); } } @@ -481,8 +456,7 @@ private static Instruction mergePrivacyConstraintsFromInput(Instruction inst, Ex } /** - * Throw exception if privacy constraint activated for instruction or for input - * to instruction. + * Throw exception if privacy constraint activated for instruction or for input to instruction. * * @param inst covariance instruction * @param ec execution context @@ -491,12 +465,12 @@ private static Instruction mergePrivacyConstraintsFromInput(Instruction inst, Ex private static Instruction throwExceptionIfInputOrInstPrivacy(Instruction inst, ExecutionContext ec) { throwExceptionIfPrivacyActivated(inst); CPOperand[] inputOperands = getInputOperands(inst); - if (inputOperands != null) { - for (CPOperand input : inputOperands) { + if(inputOperands != null) { + for(CPOperand input : inputOperands) { PrivacyConstraint privacyConstraint = getInputPrivacyConstraint(ec, input); - if (privacyConstraint != null && privacyConstraint.hasConstraints()) { + if(privacyConstraint != null && privacyConstraint.hasConstraints()) { throw new DMLPrivacyException("Input of instruction " + inst - + " has privacy constraints activated, but the constraints are not propagated during preprocessing of instruction."); + + " has privacy constraints activated, but the constraints are not propagated during preprocessing of instruction."); } } } @@ -504,15 +478,14 @@ private static Instruction throwExceptionIfInputOrInstPrivacy(Instruction inst, } private static void throwExceptionIfPrivacyActivated(Instruction inst) { - if (inst.getPrivacyConstraint() != null && inst.getPrivacyConstraint().hasConstraints()) { + if(inst.getPrivacyConstraint() != null && inst.getPrivacyConstraint().hasConstraints()) { throw new DMLPrivacyException("Instruction " + inst - + " has privacy constraints activated, but the constraints are not propagated during preprocessing of instruction."); + + " has privacy constraints activated, but the constraints are not propagated during preprocessing of instruction."); } } /** - * Propagate privacy constraint to instruction and output of instruction - * if data of first input is CacheableData and + * Propagate privacy constraint to instruction and output of instruction if data of first input is CacheableData and * privacy constraint is activated. * * @param inst VariableCPInstruction @@ -524,9 +497,8 @@ private static Instruction propagateFirstInputPrivacy(VariableCPInstruction inst } /** - * Propagate privacy constraint to instruction and output of instruction - * if data of second input is CacheableData and - * privacy constraint is activated. + * Propagate privacy constraint to instruction and output of instruction if data of second input is CacheableData + * and privacy constraint is activated. * * @param inst VariableCPInstruction * @param ec execution context @@ -537,9 +509,8 @@ private static Instruction propagateSecondInputPrivacy(VariableCPInstruction ins } /** - * Propagate privacy constraint to instruction and output of instruction - * if data of the specified variable is CacheableData - * and privacy constraint is activated + * Propagate privacy constraint to instruction and output of instruction if data of the specified variable is + * CacheableData and privacy constraint is activated * * @param inst instruction * @param ec execution context @@ -548,11 +519,11 @@ private static Instruction propagateSecondInputPrivacy(VariableCPInstruction ins * @return instruction with or without privacy constraints */ private static Instruction propagateInputPrivacy(Instruction inst, ExecutionContext ec, CPOperand inputOperand, - CPOperand outputOperand) { + CPOperand outputOperand) { PrivacyConstraint privacyConstraint = getInputPrivacyConstraint(ec, inputOperand); - if (privacyConstraint != null) { + if(privacyConstraint != null) { inst.setPrivacyConstraint(privacyConstraint); - if (outputOperand != null) + if(outputOperand != null) outputOperand.setPrivacyConstraint(privacyConstraint); } return inst; @@ -563,56 +534,52 @@ private static Instruction propagateInputPrivacy(Instruction inst, ExecutionCont * * @param ec execution context from which the data variable is retrieved * @param input data variable from which the privacy constraint is retrieved - * @return privacy constraint of variable or null if privacy constraint is not - * set + * @return privacy constraint of variable or null if privacy constraint is not set */ private static PrivacyConstraint getInputPrivacyConstraint(ExecutionContext ec, CPOperand input) { - if (input != null && input.getName() != null) { + if(input != null && input.getName() != null) { Data dd = ec.getVariable(input.getName()); - if (dd != null) + if(dd != null) return dd.getPrivacyConstraint(); } return null; } /** - * Returns input privacy constraints as array or returns null if no privacy - * constraints are found in the inputs. + * Returns input privacy constraints as array or returns null if no privacy constraints are found in the inputs. * * @param ec execution context * @param inputs from which privacy constraints are retrieved * @return array of privacy constraints from inputs */ private static PrivacyConstraint[] getInputPrivacyConstraints(ExecutionContext ec, CPOperand[] inputs) { - if (inputs != null && inputs.length > 0) { + if(inputs != null && inputs.length > 0) { boolean privacyFound = false; PrivacyConstraint[] privacyConstraints = new PrivacyConstraint[inputs.length]; - for (int i = 0; i < inputs.length; i++) { + for(int i = 0; i < inputs.length; i++) { privacyConstraints[i] = getInputPrivacyConstraint(ec, inputs[i]); - if (privacyConstraints[i] != null) + if(privacyConstraints[i] != null) privacyFound = true; } - if (privacyFound) + if(privacyFound) return privacyConstraints; } return null; } /** - * Set privacy constraint of data variable with outputName - * if the variable exists and the privacy constraint is not null. + * Set privacy constraint of data variable with outputName if the variable exists and the privacy constraint is not + * null. * - * @param ec execution context from which the data variable is - * retrieved + * @param ec execution context from which the data variable is retrieved * @param privacyConstraint privacy constraint which the variable should have - * @param outputName name of variable that is retrieved from the - * execution context + * @param outputName name of variable that is retrieved from the execution context */ private static void setOutputPrivacyConstraint(ExecutionContext ec, PrivacyConstraint privacyConstraint, - String outputName) { - if (privacyConstraint != null) { + String outputName) { + if(privacyConstraint != null) { Data dd = ec.getVariable(outputName); - if (dd != null) { + if(dd != null) { dd.setPrivacyConstraints(privacyConstraint); ec.setVariable(outputName, dd); } @@ -620,54 +587,51 @@ private static void setOutputPrivacyConstraint(ExecutionContext ec, PrivacyConst } /** - * Returns input CPOperands of instruction or returns null if instruction type - * is not supported by this method. + * Returns input CPOperands of instruction or returns null if instruction type is not supported by this method. * * @param inst instruction from which the inputs are retrieved * @return array of input CPOperands or null */ private static CPOperand[] getInputOperands(Instruction inst) { - if (inst instanceof ComputationCPInstruction) + if(inst instanceof ComputationCPInstruction) return ((ComputationCPInstruction) inst).getInputs(); - if (inst instanceof BuiltinNaryCPInstruction) + if(inst instanceof BuiltinNaryCPInstruction) return ((BuiltinNaryCPInstruction) inst).getInputs(); - if (inst instanceof FunctionCallCPInstruction) + if(inst instanceof FunctionCallCPInstruction) return ((FunctionCallCPInstruction) inst).getInputs(); - if (inst instanceof SqlCPInstruction) + if(inst instanceof SqlCPInstruction) return ((SqlCPInstruction) inst).getInputs(); else return null; } /** - * Returns a list of output CPOperands of instruction or an empty list if the - * instruction has no outputs. - * Note that this method needs to be extended as new instruction types are - * added, otherwise it will - * return an empty list for instructions that may have outputs. + * Returns a list of output CPOperands of instruction or an empty list if the instruction has no outputs. Note that + * this method needs to be extended as new instruction types are added, otherwise it will return an empty list for + * instructions that may have outputs. * * @param inst instruction from which the outputs are retrieved * @return list of outputs */ private static List getOutputOperands(Instruction inst) { // The order of the following statements is important - if (inst instanceof MultiReturnParameterizedBuiltinCPInstruction) + if(inst instanceof MultiReturnParameterizedBuiltinCPInstruction) return ((MultiReturnParameterizedBuiltinCPInstruction) inst).getOutputs(); - else if (inst instanceof MultiReturnBuiltinCPInstruction) + else if(inst instanceof MultiReturnBuiltinCPInstruction) return ((MultiReturnBuiltinCPInstruction) inst).getOutputs(); - else if (inst instanceof ComputationCPInstruction) + else if(inst instanceof ComputationCPInstruction) return getSingletonList(((ComputationCPInstruction) inst).getOutput()); - else if (inst instanceof VariableCPInstruction) + else if(inst instanceof VariableCPInstruction) return getSingletonList(((VariableCPInstruction) inst).getOutput()); - else if (inst instanceof SqlCPInstruction) + else if(inst instanceof SqlCPInstruction) return getSingletonList(((SqlCPInstruction) inst).getOutput()); - else if (inst instanceof BuiltinNaryCPInstruction) + else if(inst instanceof BuiltinNaryCPInstruction) return getSingletonList(((BuiltinNaryCPInstruction) inst).getOutput()); return new ArrayList<>(); } private static List getSingletonList(CPOperand operand) { - if (operand != null) + if(operand != null) return new ArrayList<>(Collections.singletonList(operand)); return new ArrayList<>(); } diff --git a/src/test/java/org/apache/sysds/test/component/matrix/FourierTest.java b/src/test/java/org/apache/sysds/test/component/matrix/FourierTest.java index 2b25b54c7e5..ea279ef0bcf 100644 --- a/src/test/java/org/apache/sysds/test/component/matrix/FourierTest.java +++ b/src/test/java/org/apache/sysds/test/component/matrix/FourierTest.java @@ -32,8 +32,8 @@ public class FourierTest { @Test public void test_fft_one_dim() { - MatrixBlock re = new MatrixBlock(1, 4, new double[]{0, 18, -15, 3}); - MatrixBlock im = new MatrixBlock(1, 4, new double[4]); + MatrixBlock re = new MatrixBlock(1, 4, new double[] {0, 18, -15, 3}); + MatrixBlock im = new MatrixBlock(1, 4, new double[4]); double[] expected_re = {6, 15, -36, 15}; double[] expected_im = {0, -15, 0, 15}; @@ -48,8 +48,8 @@ public void test_fft_one_dim() { @Test public void test_fft_one_dim_2() { - MatrixBlock re = new MatrixBlock(1, 8, new double[]{0, 18, -15, 3, 5, 10, 5, 9}); - MatrixBlock im = new MatrixBlock(1, 8, new double[8]); + MatrixBlock re = new MatrixBlock(1, 8, new double[] {0, 18, -15, 3, 5, 10, 5, 9}); + MatrixBlock im = new MatrixBlock(1, 8, new double[8]); double[] expected_re = {35, 4.89949, 15, -14.89949, -45, -14.89949, 15, 4.89949}; double[] expected_im = {0, 18.58579, -16, -21.41421, 0, 21.41421, 16, -18.58579}; @@ -63,8 +63,8 @@ public void test_fft_one_dim_2() { @Test public void test_fft_one_dim_matrixBlock() { - MatrixBlock re = new MatrixBlock(1, 4, new double[]{0, 18, -15, 3}); - MatrixBlock im = new MatrixBlock(1, 4, new double[]{0, 0, 0, 0}); + MatrixBlock re = new MatrixBlock(1, 4, new double[] {0, 18, -15, 3}); + MatrixBlock im = new MatrixBlock(1, 4, new double[] {0, 0, 0, 0}); double[] expected_re = {6, 15, -36, 15}; double[] expected_im = {0, -15, 0, 15}; @@ -79,8 +79,8 @@ public void test_fft_one_dim_matrixBlock() { @Test public void test_ifft_one_dim_matrixBlock_2() { - double[] in_re = new double[]{1, -2, 3, -4}; - double[] in_im = new double[]{0, 0, 0, 0}; + double[] in_re = new double[] {1, -2, 3, -4}; + double[] in_im = new double[] {0, 0, 0, 0}; MatrixBlock re = new MatrixBlock(1, 4, in_re); MatrixBlock im = new MatrixBlock(1, 4, in_im); @@ -96,10 +96,10 @@ public void test_ifft_one_dim_matrixBlock_2() { @Test public void test_fft_two_dim_matrixBlock() { - MatrixBlock re = new MatrixBlock(2, 2, new double[]{0, 18, -15, 3}); - MatrixBlock im = new MatrixBlock(2, 2, new double[]{0, 0, 0, 0}); + MatrixBlock re = new MatrixBlock(2, 2, new double[] {0, 18, -15, 3}); + MatrixBlock im = new MatrixBlock(2, 2, new double[] {0, 0, 0, 0}); - double[] expected_re = {6,-36, 30, 0}; + double[] expected_re = {6, -36, 30, 0}; double[] expected_im = {0, 0, 0, 0}; MatrixBlock[] res = fft(re, im); @@ -112,8 +112,8 @@ public void test_fft_two_dim_matrixBlock() { @Test public void test_ifft_two_dim_matrixBlock() { - MatrixBlock re = new MatrixBlock(2, 2, new double[]{6,-36, 30, 0}); - MatrixBlock im = new MatrixBlock(2, 2, new double[]{0, 0, 0, 0}); + MatrixBlock re = new MatrixBlock(2, 2, new double[] {6, -36, 30, 0}); + MatrixBlock im = new MatrixBlock(2, 2, new double[] {0, 0, 0, 0}); double[] expected_re = {0, 18, -15, 3}; double[] expected_im = {0, 0, 0, 0}; @@ -128,8 +128,8 @@ public void test_ifft_two_dim_matrixBlock() { @Test public void test_fft_two_dim_matrixBlock_row_1() { - MatrixBlock re = new MatrixBlock(1, 2, new double[]{0, 18}); - MatrixBlock im = new MatrixBlock(1, 2, new double[]{0, 0}); + MatrixBlock re = new MatrixBlock(1, 2, new double[] {0, 18}); + MatrixBlock im = new MatrixBlock(1, 2, new double[] {0, 0}); double[] expected_re = {18, -18}; double[] expected_im = {0, 0}; @@ -144,8 +144,8 @@ public void test_fft_two_dim_matrixBlock_row_1() { @Test public void test_fft_two_dim_matrixBlock_row_2() { - MatrixBlock re = new MatrixBlock(1, 2, new double[]{ -15, 3}); - MatrixBlock im = new MatrixBlock(1, 2, new double[]{0, 0}); + MatrixBlock re = new MatrixBlock(1, 2, new double[] {-15, 3}); + MatrixBlock im = new MatrixBlock(1, 2, new double[] {0, 0}); double[] expected_re = {-12, -18}; double[] expected_im = {0, 0}; @@ -161,28 +161,23 @@ public void test_fft_two_dim_matrixBlock_row_2() { public void test_ifft_with_complex_numpy_data() { // removed 0's at the end, not just real - MatrixBlock re = new MatrixBlock(1, 16, new double[]{ - 0.5398705320215192, 0.1793355360736929, 0.9065254044489506, 0.45004385530909075, - 0.11128090341119468, 0.11742862805303522, 0.7574827475752481, 0.2778193170985158, - 0.9251562928110273, 0.9414429667551927, 0.45131569795507087, 0.9522067687409731, - 0.22491032260636257, 0.6579426733967295, 0.7021558730366062, 0.7861117825617701, - }); - - MatrixBlock im = new MatrixBlock(1, 16, new double[16]); - - double[] expected_re = { - 0.5613143313659362, -0.020146500453061336, 0.07086545895481336, -0.05003801442765281, - -0.06351635451036074, -0.03346768844048936, 0.07023899089706032, 0.007330763123826495, - 0.016022890367311193, 0.007330763123826495, 0.07023899089706032, -0.03346768844048936, - -0.06351635451036074, -0.05003801442765281, 0.07086545895481336, -0.020146500453061336 - }; + MatrixBlock re = new MatrixBlock(1, 16, + new double[] {0.5398705320215192, 0.1793355360736929, 0.9065254044489506, 0.45004385530909075, + 0.11128090341119468, 0.11742862805303522, 0.7574827475752481, 0.2778193170985158, 0.9251562928110273, + 0.9414429667551927, 0.45131569795507087, 0.9522067687409731, 0.22491032260636257, 0.6579426733967295, + 0.7021558730366062, 0.7861117825617701,}); - double[] expected_im = { - 0.0, -0.07513090216687965, 0.023854392878864396, -0.018752997939582024, - -0.035626994964481226, -0.07808216015046443, 0.036579082654843526, -0.10605270957897009, - 0.0, 0.10605270957897009, -0.036579082654843526, 0.07808216015046443, - 0.035626994964481226, 0.018752997939582024, -0.023854392878864396, 0.07513090216687965 - }; + MatrixBlock im = new MatrixBlock(1, 16, new double[16]); + + double[] expected_re = {0.5613143313659362, -0.020146500453061336, 0.07086545895481336, -0.05003801442765281, + -0.06351635451036074, -0.03346768844048936, 0.07023899089706032, 0.007330763123826495, 0.016022890367311193, + 0.007330763123826495, 0.07023899089706032, -0.03346768844048936, -0.06351635451036074, -0.05003801442765281, + 0.07086545895481336, -0.020146500453061336}; + + double[] expected_im = {0.0, -0.07513090216687965, 0.023854392878864396, -0.018752997939582024, + -0.035626994964481226, -0.07808216015046443, 0.036579082654843526, -0.10605270957897009, 0.0, + 0.10605270957897009, -0.036579082654843526, 0.07808216015046443, 0.035626994964481226, 0.018752997939582024, + -0.023854392878864396, 0.07513090216687965}; MatrixBlock[] res = ifft(re, im); @@ -194,22 +189,16 @@ public void test_ifft_with_complex_numpy_data() { @Test public void test_ifft_2d_with_generated_data() { - MatrixBlock re = new MatrixBlock(2, 2, new double[]{ - 0.6749989259154331, 0.6278845555308362, 0.995916990652601, 0.511472971081564 - }); + MatrixBlock re = new MatrixBlock(2, 2, + new double[] {0.6749989259154331, 0.6278845555308362, 0.995916990652601, 0.511472971081564}); - MatrixBlock im = new MatrixBlock(2, 2, new double[]{ - 0.8330832079105173, 0.09857986129294982, 0.6808883894146879, 0.28353782431047303 - }); + MatrixBlock im = new MatrixBlock(2, 2, + new double[] {0.8330832079105173, 0.09857986129294982, 0.6808883894146879, 0.28353782431047303}); // adjusted the expected output - double[] expected_re = { - 0.70256836, 0.1328896, -0.05112662, -0.10933241 - }; + double[] expected_re = {0.70256836, 0.1328896, -0.05112662, -0.10933241}; - double[] expected_im = { - 0.47402232, 0.28296348, -0.00819079, 0.0842882 - }; + double[] expected_im = {0.47402232, 0.28296348, -0.00819079, 0.0842882}; MatrixBlock[] res = ifft(re, im); @@ -222,28 +211,21 @@ public void test_ifft_2d_with_generated_data() { public void test_ifft_with_real_numpy_data() { // not complex - MatrixBlock re = new MatrixBlock(1, 16, new double[]{ - 0.17768499045697306, 0.3405035491673728, 0.9272906450602005, 0.28247504052271843, - 0.42775517613102865, 0.8338783039818357, 0.9813749624557385, 0.47224489612381737, - 0.7936831995784907, 0.5584182145651306, 0.5296113722056018, 0.6687593295928902, - 0.9630598447622862, 0.7130539473424196, 0.860081483892192, 0.8985058305053549 - }); + MatrixBlock re = new MatrixBlock(1, 16, + new double[] {0.17768499045697306, 0.3405035491673728, 0.9272906450602005, 0.28247504052271843, + 0.42775517613102865, 0.8338783039818357, 0.9813749624557385, 0.47224489612381737, 0.7936831995784907, + 0.5584182145651306, 0.5296113722056018, 0.6687593295928902, 0.9630598447622862, 0.7130539473424196, + 0.860081483892192, 0.8985058305053549}); - MatrixBlock im = new MatrixBlock(1, 16, new double[16]); + MatrixBlock im = new MatrixBlock(1, 16, new double[16]); // adjusted the expected output - double[] expected_re = { - 0.6517738, -0.0263837 , -0.03631354, -0.01644966, - -0.05851095, -0.0849794, -0.01611732, -0.02618679, - 0.05579391, -0.02618679, -0.01611732, -0.0849794, - -0.05851095, -0.01644966, -0.03631354, -0.0263837 - }; + double[] expected_re = {0.6517738, -0.0263837, -0.03631354, -0.01644966, -0.05851095, -0.0849794, -0.01611732, + -0.02618679, 0.05579391, -0.02618679, -0.01611732, -0.0849794, -0.05851095, -0.01644966, -0.03631354, + -0.0263837}; - double[] expected_im = { - 0, -0.04125649, -0.07121312, 0.02554502, - 0.00774181, -0.08723921, -0.02314382, -0.02021455, - 0, 0.02021455, 0.02314382, 0.08723921, - -0.00774181, -0.02554502, 0.07121312, 0.04125649 + double[] expected_im = {0, -0.04125649, -0.07121312, 0.02554502, 0.00774181, -0.08723921, -0.02314382, + -0.02021455, 0, 0.02021455, 0.02314382, 0.08723921, -0.00774181, -0.02554502, 0.07121312, 0.04125649 }; @@ -257,81 +239,64 @@ public void test_ifft_with_real_numpy_data() { @Test public void test_fft_two_dim_8_times_8() { - MatrixBlock re = new MatrixBlock(8, 8, new double[]{ - 0.8435874964408077, 0.3565209485970835, 0.6221038572251737, 0.05712418097055716, - 0.9301368966310067, 0.7748052735242277, 0.21117129518682443, 0.08407931152930459, - 0.5861235649815163, 0.45860122035396356, 0.6647476180103304, 0.9167930424492593, - 0.6310726270028377, 0.11110504251770592, 0.32369996452324756, 0.5790548902504138, - 0.5712310851880162, 0.5967356161025353, 0.6441861776319489, 0.14402445187596158, - 0.22642623625293545, 0.922443731897705, 0.9527667119829785, 0.2250880965427453, - 0.5755375055168817, 0.48898427237526954, 0.24518238824389693, 0.832292384016089, - 0.23789083930394805, 0.5558982102157535, 0.7220016080026206, 0.9666747522359772, - 0.20509423975210916, 0.23170117015755587, 0.7141206714718693, 0.2014158450611332, - 0.6486924358372994, 0.9044990419216931, 0.19849364935627056, 0.23340297110822106, - 0.46854050631969246, 0.10134155509558795, 0.5563200388698989, 0.2669820016661475, - 0.8889445005077763, 0.4273462470993935, 0.8269490075576963, 0.044351336481537995, - 0.3771564738915597, 0.11333723996854606, 0.6913138435759023, 0.062431275099310124, - 0.8003013976959878, 0.1276686539064662, 0.975167392001707, 0.44595301043682656, - 0.18401328301977316, 0.7158585484384759, 0.3240126702723025, 0.740836665073052, - 0.8890279623888511, 0.8841266040978419, 0.3058930798936259, 0.8987579873722049 - }); - - MatrixBlock im = new MatrixBlock(1, 16, new double[]{ - 0.8572457113722648, 0.668182795310341, 0.9739416721141464, 0.8189153345383146, - 0.6425950286263254, 0.3569634253534639, 0.19715070300424575, 0.8915344479242211, - 0.39207930659031054, 0.1625193685179268, 0.2523438052868171, 0.30940628850519547, - 0.7461468672112159, 0.7123766750132684, 0.5261041429273977, 0.867155304805022, - 0.7207769261821749, 0.9139070611733158, 0.7638265842242135, 0.3508092733308539, - 0.6075639148195967, 0.9615531048215422, 0.719499617407839, 0.9616615941848492, - 0.2667126256574347, 0.8215093145949468, 0.4240476512138287, 0.5015798652459079, - 0.19784651066995873, 0.42315603332105356, 0.5575575283922164, 0.9051304828282485, - 0.30117855478511435, 0.14219967492505514, 0.32675429179906557, 0.04889894374947912, - 0.8338579676700041, 0.370201089804747, 0.06025987717830994, 0.9407970353033787, - 0.9871788482561391, 0.75984297199074, 0.414969247979073, 0.2453785474698862, - 0.06295683447294731, 0.40141192931768566, 0.19520663793867488, 0.3179027928938928, - 0.591138083168947, 0.5318366162549014, 0.04865894304644136, 0.5339043989658795, - 0.09892519435896363, 0.31616794516128466, 0.06702286400447643, 0.8466767273121609, - 0.8134875055724791, 0.6232554321597641, 0.21208039111457444, 0.25629831822305926, - 0.7373140896724466, 0.020486629088602437, 0.8666668269441752, 0.20094387974200512 - }); - - double[] expected_re = { - 32.51214260297584, -3.4732779237490314, -0.7257760912890102, -1.9627494786611792, - 3.571671446098747, 1.0451692206901078, 0.8970702451384204, -1.3739767803210428, - 4.892442103095981, -1.1656855109832338, -0.5854908742291178, -1.3497699084098418, - -1.377003693155216, -2.1698030461214923, 0.8172129683973663, -0.9259076518379679, - -1.1343756245445045, -1.8734967800709579, 1.7367517585478862, 0.07349671655414491, - -1.5933768052439223, 2.7965196291943983, 4.292588673604611, -1.1032899622026413, - -2.4643093702874834, 2.109128987930992, 3.2834030498896456, 0.21371926254596152, - -0.3107488550365316, 0.7293395030253796, -2.542403789759091, -1.8570654162590052, - -2.325781245331303, 0.7963395911053484, -2.351990667205867, -2.4241188304485735, - 4.689766636746301, 3.4748121457306116, 0.5539071663846459, -0.950099313504134, - 2.122310975524349, 4.527637759721644, -2.125596093625001, 1.7676565539001592, - 5.748332643019926, 0.860140632830907, 2.9735142186218484, -1.7198774815194848, - -0.18418859401548549, 1.1909629561342188, 0.21710627714418418, -1.5537184277268996, - -0.5486540814747869, 0.14807346060743987, 2.4333154010438087, -3.2930077637380393, - -2.3820067665775113, 2.5463581304688057, 2.5927580716559615, 1.8921802492721915, - 0.4957713559465988, -0.4983536537999108, 3.5808175362367676, 0.7530823235547575 - }; - - double[] expected_im = { - 32.64565805549281, 5.177639365468945, 1.1792020104097647, -0.4850627423320939, - -1.719468548169175, -3.064146170894837, 3.3226243586118906, 2.3819341640916107, - 1.5824429804361388, -2.192940882164737, 0.5774407122593543, 0.16873948200103983, - 4.297014293326352, -3.1712082122026883, 0.9741291131305898, -2.4929883795121235, - 1.111763301820595, -1.4012254390671657, -0.33687898317382636, 2.324190267133635, - -2.8862969254091397, -4.7558982401265135, 1.8244587481290004, 0.5310550630270396, - 2.655726742689745, 2.510014260306531, 0.25589537824783704, 1.8720307201415736, - -2.6458046644482884, 2.1732611302115585, -2.5162250969793227, -0.9103444457919911, - 2.2835527482590248, 0.5187392677625127, -3.335253420903965, 1.4668560670097441, - -1.9681585205341436, -2.81914578771063, 4.818094364700921, -0.877636803126361, - 1.803174743823159, 3.1346192487664277, -3.564058675191744, -0.3391381913837902, - -1.2897867384105863, 2.315065426377637, 1.5764817121472765, 2.412894091248795, - -2.3182678917385218, -3.057303547366563, 0.033996764974414395, -0.5825423640666696, - 6.395088232674363, -0.5553659624089358, 1.1079219041153268, 0.1094531803830765, - 3.488182265163636, -1.5698242466218544, -0.1013387045518459, 0.9269290699615746, - -0.699890233104248, 3.617209720991753, -0.5565163478425035, 3.502962737763559 - }; + MatrixBlock re = new MatrixBlock(8, 8, + new double[] {0.8435874964408077, 0.3565209485970835, 0.6221038572251737, 0.05712418097055716, + 0.9301368966310067, 0.7748052735242277, 0.21117129518682443, 0.08407931152930459, 0.5861235649815163, + 0.45860122035396356, 0.6647476180103304, 0.9167930424492593, 0.6310726270028377, 0.11110504251770592, + 0.32369996452324756, 0.5790548902504138, 0.5712310851880162, 0.5967356161025353, 0.6441861776319489, + 0.14402445187596158, 0.22642623625293545, 0.922443731897705, 0.9527667119829785, 0.2250880965427453, + 0.5755375055168817, 0.48898427237526954, 0.24518238824389693, 0.832292384016089, 0.23789083930394805, + 0.5558982102157535, 0.7220016080026206, 0.9666747522359772, 0.20509423975210916, 0.23170117015755587, + 0.7141206714718693, 0.2014158450611332, 0.6486924358372994, 0.9044990419216931, 0.19849364935627056, + 0.23340297110822106, 0.46854050631969246, 0.10134155509558795, 0.5563200388698989, 0.2669820016661475, + 0.8889445005077763, 0.4273462470993935, 0.8269490075576963, 0.044351336481537995, 0.3771564738915597, + 0.11333723996854606, 0.6913138435759023, 0.062431275099310124, 0.8003013976959878, 0.1276686539064662, + 0.975167392001707, 0.44595301043682656, 0.18401328301977316, 0.7158585484384759, 0.3240126702723025, + 0.740836665073052, 0.8890279623888511, 0.8841266040978419, 0.3058930798936259, 0.8987579873722049}); + + MatrixBlock im = new MatrixBlock(1, 16, + new double[] {0.8572457113722648, 0.668182795310341, 0.9739416721141464, 0.8189153345383146, + 0.6425950286263254, 0.3569634253534639, 0.19715070300424575, 0.8915344479242211, 0.39207930659031054, + 0.1625193685179268, 0.2523438052868171, 0.30940628850519547, 0.7461468672112159, 0.7123766750132684, + 0.5261041429273977, 0.867155304805022, 0.7207769261821749, 0.9139070611733158, 0.7638265842242135, + 0.3508092733308539, 0.6075639148195967, 0.9615531048215422, 0.719499617407839, 0.9616615941848492, + 0.2667126256574347, 0.8215093145949468, 0.4240476512138287, 0.5015798652459079, 0.19784651066995873, + 0.42315603332105356, 0.5575575283922164, 0.9051304828282485, 0.30117855478511435, 0.14219967492505514, + 0.32675429179906557, 0.04889894374947912, 0.8338579676700041, 0.370201089804747, 0.06025987717830994, + 0.9407970353033787, 0.9871788482561391, 0.75984297199074, 0.414969247979073, 0.2453785474698862, + 0.06295683447294731, 0.40141192931768566, 0.19520663793867488, 0.3179027928938928, 0.591138083168947, + 0.5318366162549014, 0.04865894304644136, 0.5339043989658795, 0.09892519435896363, 0.31616794516128466, + 0.06702286400447643, 0.8466767273121609, 0.8134875055724791, 0.6232554321597641, 0.21208039111457444, + 0.25629831822305926, 0.7373140896724466, 0.020486629088602437, 0.8666668269441752, + 0.20094387974200512}); + + double[] expected_re = {32.51214260297584, -3.4732779237490314, -0.7257760912890102, -1.9627494786611792, + 3.571671446098747, 1.0451692206901078, 0.8970702451384204, -1.3739767803210428, 4.892442103095981, + -1.1656855109832338, -0.5854908742291178, -1.3497699084098418, -1.377003693155216, -2.1698030461214923, + 0.8172129683973663, -0.9259076518379679, -1.1343756245445045, -1.8734967800709579, 1.7367517585478862, + 0.07349671655414491, -1.5933768052439223, 2.7965196291943983, 4.292588673604611, -1.1032899622026413, + -2.4643093702874834, 2.109128987930992, 3.2834030498896456, 0.21371926254596152, -0.3107488550365316, + 0.7293395030253796, -2.542403789759091, -1.8570654162590052, -2.325781245331303, 0.7963395911053484, + -2.351990667205867, -2.4241188304485735, 4.689766636746301, 3.4748121457306116, 0.5539071663846459, + -0.950099313504134, 2.122310975524349, 4.527637759721644, -2.125596093625001, 1.7676565539001592, + 5.748332643019926, 0.860140632830907, 2.9735142186218484, -1.7198774815194848, -0.18418859401548549, + 1.1909629561342188, 0.21710627714418418, -1.5537184277268996, -0.5486540814747869, 0.14807346060743987, + 2.4333154010438087, -3.2930077637380393, -2.3820067665775113, 2.5463581304688057, 2.5927580716559615, + 1.8921802492721915, 0.4957713559465988, -0.4983536537999108, 3.5808175362367676, 0.7530823235547575}; + + double[] expected_im = {32.64565805549281, 5.177639365468945, 1.1792020104097647, -0.4850627423320939, + -1.719468548169175, -3.064146170894837, 3.3226243586118906, 2.3819341640916107, 1.5824429804361388, + -2.192940882164737, 0.5774407122593543, 0.16873948200103983, 4.297014293326352, -3.1712082122026883, + 0.9741291131305898, -2.4929883795121235, 1.111763301820595, -1.4012254390671657, -0.33687898317382636, + 2.324190267133635, -2.8862969254091397, -4.7558982401265135, 1.8244587481290004, 0.5310550630270396, + 2.655726742689745, 2.510014260306531, 0.25589537824783704, 1.8720307201415736, -2.6458046644482884, + 2.1732611302115585, -2.5162250969793227, -0.9103444457919911, 2.2835527482590248, 0.5187392677625127, + -3.335253420903965, 1.4668560670097441, -1.9681585205341436, -2.81914578771063, 4.818094364700921, + -0.877636803126361, 1.803174743823159, 3.1346192487664277, -3.564058675191744, -0.3391381913837902, + -1.2897867384105863, 2.315065426377637, 1.5764817121472765, 2.412894091248795, -2.3182678917385218, + -3.057303547366563, 0.033996764974414395, -0.5825423640666696, 6.395088232674363, -0.5553659624089358, + 1.1079219041153268, 0.1094531803830765, 3.488182265163636, -1.5698242466218544, -0.1013387045518459, + 0.9269290699615746, -0.699890233104248, 3.617209720991753, -0.5565163478425035, 3.502962737763559}; MatrixBlock[] res = fft(re, im); diff --git a/src/test/java/org/apache/sysds/test/component/matrix/FourierTestLarge.java b/src/test/java/org/apache/sysds/test/component/matrix/FourierTestLarge.java index cc011453afb..478ba0c8aa2 100644 --- a/src/test/java/org/apache/sysds/test/component/matrix/FourierTestLarge.java +++ b/src/test/java/org/apache/sysds/test/component/matrix/FourierTestLarge.java @@ -26,1495 +26,1355 @@ import static org.apache.sysds.runtime.matrix.data.LibMatrixFourier.ifft; public class FourierTestLarge { - int progressInterval = 5000; + int progressInterval = 5000; - // prior to executing the following tests it is necessary to run the Numpy - // Script in FourierTestData.py - // and add the generated files to the root of the project. - @Test - public void testFftWithGeneratedData() { - // Define the input data array (first 1000 inputs) - double[][] inputData = { - { 0.307856129173271, 0.08921910198690264, 0.618009230694374, 0.5313115890139828, 0.8321948342164646, - 0.5931468404266381, 0.6183643824098176, 0.09098483235809818, 0.020002539689180643, - 0.9179524283672298, 0.6092688826975122, 0.03765800262802799, 0.6583959135030817, - 0.346267450443292, 0.9794472087243126, 0.8642963614250007 }, - { 0.997301378000895, 0.3502640931966453, 0.1954636101454681, 0.10391072327500839, 0.21327031067780433, - 0.0643272237899376, 0.8221952489149892, 0.9534348411569122, 0.9534997101452614, - 0.5141779472770125, 0.2306040802116438, 0.8386736679248497, 0.0981318516277605, - 0.8074595469184841, 0.254078430810886, 0.5996117331901718 }, - { 0.870957838868506, 0.912745725021149, 0.5345683140982496, 0.18114400014042686, 0.5931978577526508, - 0.14192088683195558, 0.9208741124169134, 0.6525015591512732 }, - { 0.29788916579478153, 0.27748600931023504, 0.4853091738976063, 0.8567045012601394 }, - { 0.6352092395258702, 0.43145575925660073 }, - { 0.5617699291201627, 0.21489241033852313 }, - { 0.6576030646834683, 0.9290768001953902, 0.09848637471541899, 0.06425572022630976, 0.5396213600686569, - 0.22085716679561374, 0.7272917506111063, 0.14096681648120335, 0.003323239715782522, - 0.29724749937056516, 0.2356003703231878, 0.4343299880624373, 0.6875247706424916, - 0.8485613871894802, 0.9773614895293514, 0.48945201661534776 }, - { 0.8821994027742829, 0.21163278841593125, 0.4932536463151129, 0.48650972337895804, 0.7786999918312242, - 0.14154217886198583, 0.050381939456006886, 0.4817754647240189, 0.25197827244265536, - 0.19196634342654173, 0.2248835944599762, 0.09610110952097772, 0.9234776641814892, - 0.8345039754950259, 0.9145560074968945, 0.2448292011827906 }, - { 0.7385591035373894, 0.6934429707180507, 0.20469771060726105, 0.7146443181329221 }, - { 0.09423365095676517, 0.4200805363713518, 0.8898189099184024, 0.15185704349763007, 0.976019182222216, - 0.26121755769518185, 0.07688163464324083, 0.5605512821290499, 0.01968958370279128, - 0.02586318786500097, 0.13903676787373986, 0.29289492175451814, 0.9598461594746724, - 0.06685602573191851, 0.8132935172237855, 0.07525352129151597 }, - { 0.06574341421208751, 0.8492596587119035, 0.8065274870296378, 0.27175430100069153, 0.2954082423553953, - 0.3563055835463892, 0.5417770631268415, 0.9615039909442821 }, - { 0.04211428054995747, 0.7911133121974274, 0.2118116815925455, 0.483908183679128, 0.9084187853511388, - 0.6796408581806436, 0.10941522676794624, 0.7455635937529194, 0.620135811235268, - 0.7769721780197776, 0.7167767032200226, 0.10390503304337428, 0.6407001194530335, - 0.26083464261776423, 0.5806294628171111, 0.2518664967318035 }, - { 0.18129008646882727, 0.8669649844463896, 0.028246543418389947, 0.48363667274187305, - 0.7943230293132295, 0.6961876877536417, 0.4275685366852513, 0.45634721662560607, - 0.1005692992795224, 0.9050284283882628, 0.3146718845340458, 0.7861414010588036, - 0.4770427042197599, 0.1462914941041602, 0.3965644055730563, 0.9153636362926348 }, - { 0.8161580934614122, 0.18477962701579598 }, - { 0.08028451743536968, 0.24496797132982906, 0.0849486774071061, 0.7881678651794348, 0.32208849264834005, - 0.027946650916384486, 0.6481634237831947, 0.07243767209378071, 0.5807489567390403, - 0.852034426396901, 0.7611516595374496, 0.05624561395049921, 0.2909447900344724, - 0.02896220325285881, 0.6042269314873742, 0.709088439526459 }, - { 0.9419390726681351, 0.25416475350108325, 0.3668196423318234, 0.0004937686334369751, - 0.45115568384401916, 0.15272066875461188, 0.13006769168951093, 0.008318934989370197, - 0.7881465440955732, 0.933662847575916, 0.08386521463152519, 0.6529529591688511, - 0.4473354453449845, 0.31327384554825755, 0.5058970453705052, 0.870525057224827 }, - { 0.5505180216822709, 0.5781501773859546 }, - { 0.8956572090779427, 0.8299050830853423, 0.1424103295292285, 0.13728455802847184, 0.8000171582078991, - 0.6298805318267743, 0.46391319943080855, 0.5663723332416153 }, - { 0.9683653719909785, 0.2232641902635809, 0.6148539423093378, 0.37453351154356274 }, - { 0.7067035754354158, 0.0663739473919116 }, - { 0.47086229838998894, 0.8960946381686666, 0.9533858690069733, 0.5176638484634724, 0.015341863574319992, - 0.09419290215121512, 0.7068849515138174, 0.2050445924658565 }, - { 0.5785763844543972, 0.9176187041034242 }, - { 0.5241184105276374, 0.2509183778867127, 0.12853860707933074, 0.9590386361137108 }, - { 0.8571553248185413, 0.6263814942317649, 0.95881731622584, 0.310415053009538, 0.4775273408541796, - 0.6329378690509789, 0.7178046354589865, 0.9923541273411732, 0.44017806754392763, - 0.2522486894328567, 0.12849993855560982, 0.8988211375767242, 0.3617603937666176, - 0.10711880543199137, 0.9905379056406248, 0.8906739530932947 }, - { 0.8536820346534935, 0.5656301408899768, 0.24077798818075857, 0.34595328041358364, 0.8821071518415022, - 0.5357200569496784, 0.13935877170910727, 0.2502969398391611 }, - { 0.7293440752321705, 0.335697322075603, 0.7609624135786428, 0.5430882990642741, 0.7903417394181252, - 0.09592791445258453, 0.8251861573566924, 0.5242152290578941, 0.5274752317660253, - 0.5978730300121011, 0.9257445772312801, 0.23083171339790576, 0.4992817006893182, - 0.7009970366772414, 0.09991780231136771, 0.19590332619221507 }, - { 0.45683639103550133, 0.4127698308179104, 0.15849785349035195, 0.23820398589917613, - 0.42556319726902625, 0.3902556783233726, 0.44922620996988205, 0.47889491731179334, - 0.31088942492181826, 0.8561445618860377, 0.35456306768933543, 0.6656974246709467, - 0.60674113513703, 0.4713576352952902, 0.7389009344705372, 0.8211538788821755 }, - { 0.18081479839397807, 0.47100279012993196 }, - { 0.21954540433093261, 0.40458389194582534, 0.7208723075287345, 0.8846816526712216, 0.5327808546064456, - 0.8864372849612085, 0.05751443204835982, 0.807453849418354, 0.9593743129540142, - 0.6715092386060035, 0.7003192281171006, 0.5476351764742082, 0.7413425202579201, - 0.3598546138493517, 0.1710023879149254, 0.3477614210012976 }, - { 0.04105479173235693, 0.2790129805889898 }, - { 0.31270679654880107, 0.6563226858676515, 0.065309659982147, 0.9594045059676773, 0.41461631741634075, - 0.8549726106226234, 0.6885625924540151, 0.34077133669639903 }, - { 0.05216212273283238, 0.1663159578169714, 0.6755000798755829, 0.8410660871499066 }, - { 0.43067498044817587, 0.38256389857433204 }, - { 0.637327693590928, 0.3133763054769817, 0.016610252725152153, 0.32652177024153395, - 0.009691976865415008, 0.23600780862854676, 0.8836055793679196, 0.45244777988456386 }, - { 0.5906578759363037, 0.7944662801327856, 0.17863645183223031, 0.32277739351179, 0.3231823947395689, - 0.4034735202642745, 0.06399390314662634, 0.17540386741349312 }, - { 0.17815921306628235, 0.8585869087579457 }, - { 0.5871751868409761, 0.6534562814682419 }, - { 0.39586773608713244, 0.7387586667130125, 0.19184209782072326, 0.10410270481271588, 0.9951764576402166, - 0.23388910739629953, 0.7647794115198386, 0.8052705154780063 }, - { 0.6585162649365032, 0.711175495841072, 0.4676580282401064, 0.8826289448730669, 0.82861818848699, - 0.9426098006296768, 0.9615519675050708, 0.40172361372445387 }, - { 0.554938210317202, 0.45311686056607847, 0.848278396952335, 0.9663886131569552, 0.993175945037519, - 0.4321940233579258, 0.9786877966394255, 0.22383267497846604 }, - { 0.14832468791702524, 0.7911386648581192, 0.4342135601328583, 0.39064350292654393, 0.00154990539545663, - 0.7056390559656973, 0.5449016159778458, 0.44995063726730145 }, - { 0.6787570562911824, 0.2108141544985952, 0.36959074400723846, 0.6065094525663848, 0.14370437302298023, - 0.7471705670569604, 0.3991987649234662, 0.6377650280189965, 0.7966311569652879, - 0.6699688376083757, 0.11046232705364989, 0.0343797538976689, 0.4263281825639861, - 0.06018487827135577, 0.27543446279089245, 0.6307832714073219 }, - { 0.007401290917390502, 0.8640389112554114, 0.08779720334707941, 0.6587228816523323, - 0.21985520800643954, 0.4963969113562372, 0.25041837572771375, 0.2996763341721056 }, - { 0.9283706112012573, 0.9008656544411213, 0.01039871402142889, 0.637525445174257 }, - { 0.838047562953646, 0.9020667732919446 }, - { 0.7455441724472716, 0.5020772412030806, 0.10951344204683389, 0.30933274344662487 }, - { 0.8180656768575455, 0.8666279680604059, 0.42583580021283085, 0.1433193988956274, 0.8669332518159855, - 0.7414686722465978, 0.5939337651104747, 0.6789552509532184 }, - { 0.48808916431556837, 0.8241477106539906, 0.7262098103482119, 0.899834357237205, 0.8834046134657804, - 0.6029189784057519, 0.46118570354302213, 0.20904657865994403, 0.17528071980731652, - 0.9766278629190644, 0.2643652212919221, 0.22466069704047253, 0.5868640515110155, - 0.40572150873462176, 0.012091970132293572, 0.7702657347835162 }, - { 0.8296574984897637, 0.2009561060794306, 0.4599409596490742, 0.2981967624462212 }, - { 0.9517083930235997, 0.9818426792525043 } - }; + // prior to executing the following tests it is necessary to run the Numpy + // Script in FourierTestData.py + // and add the generated files to the root of the project. + @Test + public void testFftWithGeneratedData() { + // Define the input data array (first 1000 inputs) + double[][] inputData = { + {0.307856129173271, 0.08921910198690264, 0.618009230694374, 0.5313115890139828, 0.8321948342164646, + 0.5931468404266381, 0.6183643824098176, 0.09098483235809818, 0.020002539689180643, 0.9179524283672298, + 0.6092688826975122, 0.03765800262802799, 0.6583959135030817, 0.346267450443292, 0.9794472087243126, + 0.8642963614250007}, + {0.997301378000895, 0.3502640931966453, 0.1954636101454681, 0.10391072327500839, 0.21327031067780433, + 0.0643272237899376, 0.8221952489149892, 0.9534348411569122, 0.9534997101452614, 0.5141779472770125, + 0.2306040802116438, 0.8386736679248497, 0.0981318516277605, 0.8074595469184841, 0.254078430810886, + 0.5996117331901718}, + {0.870957838868506, 0.912745725021149, 0.5345683140982496, 0.18114400014042686, 0.5931978577526508, + 0.14192088683195558, 0.9208741124169134, 0.6525015591512732}, + {0.29788916579478153, 0.27748600931023504, 0.4853091738976063, 0.8567045012601394}, + {0.6352092395258702, 0.43145575925660073}, {0.5617699291201627, 0.21489241033852313}, + {0.6576030646834683, 0.9290768001953902, 0.09848637471541899, 0.06425572022630976, 0.5396213600686569, + 0.22085716679561374, 0.7272917506111063, 0.14096681648120335, 0.003323239715782522, 0.29724749937056516, + 0.2356003703231878, 0.4343299880624373, 0.6875247706424916, 0.8485613871894802, 0.9773614895293514, + 0.48945201661534776}, + {0.8821994027742829, 0.21163278841593125, 0.4932536463151129, 0.48650972337895804, 0.7786999918312242, + 0.14154217886198583, 0.050381939456006886, 0.4817754647240189, 0.25197827244265536, 0.19196634342654173, + 0.2248835944599762, 0.09610110952097772, 0.9234776641814892, 0.8345039754950259, 0.9145560074968945, + 0.2448292011827906}, + {0.7385591035373894, 0.6934429707180507, 0.20469771060726105, 0.7146443181329221}, + {0.09423365095676517, 0.4200805363713518, 0.8898189099184024, 0.15185704349763007, 0.976019182222216, + 0.26121755769518185, 0.07688163464324083, 0.5605512821290499, 0.01968958370279128, 0.02586318786500097, + 0.13903676787373986, 0.29289492175451814, 0.9598461594746724, 0.06685602573191851, 0.8132935172237855, + 0.07525352129151597}, + {0.06574341421208751, 0.8492596587119035, 0.8065274870296378, 0.27175430100069153, 0.2954082423553953, + 0.3563055835463892, 0.5417770631268415, 0.9615039909442821}, + {0.04211428054995747, 0.7911133121974274, 0.2118116815925455, 0.483908183679128, 0.9084187853511388, + 0.6796408581806436, 0.10941522676794624, 0.7455635937529194, 0.620135811235268, 0.7769721780197776, + 0.7167767032200226, 0.10390503304337428, 0.6407001194530335, 0.26083464261776423, 0.5806294628171111, + 0.2518664967318035}, + {0.18129008646882727, 0.8669649844463896, 0.028246543418389947, 0.48363667274187305, 0.7943230293132295, + 0.6961876877536417, 0.4275685366852513, 0.45634721662560607, 0.1005692992795224, 0.9050284283882628, + 0.3146718845340458, 0.7861414010588036, 0.4770427042197599, 0.1462914941041602, 0.3965644055730563, + 0.9153636362926348}, + {0.8161580934614122, 0.18477962701579598}, + {0.08028451743536968, 0.24496797132982906, 0.0849486774071061, 0.7881678651794348, 0.32208849264834005, + 0.027946650916384486, 0.6481634237831947, 0.07243767209378071, 0.5807489567390403, 0.852034426396901, + 0.7611516595374496, 0.05624561395049921, 0.2909447900344724, 0.02896220325285881, 0.6042269314873742, + 0.709088439526459}, + {0.9419390726681351, 0.25416475350108325, 0.3668196423318234, 0.0004937686334369751, 0.45115568384401916, + 0.15272066875461188, 0.13006769168951093, 0.008318934989370197, 0.7881465440955732, 0.933662847575916, + 0.08386521463152519, 0.6529529591688511, 0.4473354453449845, 0.31327384554825755, 0.5058970453705052, + 0.870525057224827}, + {0.5505180216822709, 0.5781501773859546}, + {0.8956572090779427, 0.8299050830853423, 0.1424103295292285, 0.13728455802847184, 0.8000171582078991, + 0.6298805318267743, 0.46391319943080855, 0.5663723332416153}, + {0.9683653719909785, 0.2232641902635809, 0.6148539423093378, 0.37453351154356274}, + {0.7067035754354158, 0.0663739473919116}, + {0.47086229838998894, 0.8960946381686666, 0.9533858690069733, 0.5176638484634724, 0.015341863574319992, + 0.09419290215121512, 0.7068849515138174, 0.2050445924658565}, + {0.5785763844543972, 0.9176187041034242}, + {0.5241184105276374, 0.2509183778867127, 0.12853860707933074, 0.9590386361137108}, + {0.8571553248185413, 0.6263814942317649, 0.95881731622584, 0.310415053009538, 0.4775273408541796, + 0.6329378690509789, 0.7178046354589865, 0.9923541273411732, 0.44017806754392763, 0.2522486894328567, + 0.12849993855560982, 0.8988211375767242, 0.3617603937666176, 0.10711880543199137, 0.9905379056406248, + 0.8906739530932947}, + {0.8536820346534935, 0.5656301408899768, 0.24077798818075857, 0.34595328041358364, 0.8821071518415022, + 0.5357200569496784, 0.13935877170910727, 0.2502969398391611}, + {0.7293440752321705, 0.335697322075603, 0.7609624135786428, 0.5430882990642741, 0.7903417394181252, + 0.09592791445258453, 0.8251861573566924, 0.5242152290578941, 0.5274752317660253, 0.5978730300121011, + 0.9257445772312801, 0.23083171339790576, 0.4992817006893182, 0.7009970366772414, 0.09991780231136771, + 0.19590332619221507}, + {0.45683639103550133, 0.4127698308179104, 0.15849785349035195, 0.23820398589917613, 0.42556319726902625, + 0.3902556783233726, 0.44922620996988205, 0.47889491731179334, 0.31088942492181826, 0.8561445618860377, + 0.35456306768933543, 0.6656974246709467, 0.60674113513703, 0.4713576352952902, 0.7389009344705372, + 0.8211538788821755}, + {0.18081479839397807, 0.47100279012993196}, + {0.21954540433093261, 0.40458389194582534, 0.7208723075287345, 0.8846816526712216, 0.5327808546064456, + 0.8864372849612085, 0.05751443204835982, 0.807453849418354, 0.9593743129540142, 0.6715092386060035, + 0.7003192281171006, 0.5476351764742082, 0.7413425202579201, 0.3598546138493517, 0.1710023879149254, + 0.3477614210012976}, + {0.04105479173235693, 0.2790129805889898}, + {0.31270679654880107, 0.6563226858676515, 0.065309659982147, 0.9594045059676773, 0.41461631741634075, + 0.8549726106226234, 0.6885625924540151, 0.34077133669639903}, + {0.05216212273283238, 0.1663159578169714, 0.6755000798755829, 0.8410660871499066}, + {0.43067498044817587, 0.38256389857433204}, + {0.637327693590928, 0.3133763054769817, 0.016610252725152153, 0.32652177024153395, 0.009691976865415008, + 0.23600780862854676, 0.8836055793679196, 0.45244777988456386}, + {0.5906578759363037, 0.7944662801327856, 0.17863645183223031, 0.32277739351179, 0.3231823947395689, + 0.4034735202642745, 0.06399390314662634, 0.17540386741349312}, + {0.17815921306628235, 0.8585869087579457}, {0.5871751868409761, 0.6534562814682419}, + {0.39586773608713244, 0.7387586667130125, 0.19184209782072326, 0.10410270481271588, 0.9951764576402166, + 0.23388910739629953, 0.7647794115198386, 0.8052705154780063}, + {0.6585162649365032, 0.711175495841072, 0.4676580282401064, 0.8826289448730669, 0.82861818848699, + 0.9426098006296768, 0.9615519675050708, 0.40172361372445387}, + {0.554938210317202, 0.45311686056607847, 0.848278396952335, 0.9663886131569552, 0.993175945037519, + 0.4321940233579258, 0.9786877966394255, 0.22383267497846604}, + {0.14832468791702524, 0.7911386648581192, 0.4342135601328583, 0.39064350292654393, 0.00154990539545663, + 0.7056390559656973, 0.5449016159778458, 0.44995063726730145}, + {0.6787570562911824, 0.2108141544985952, 0.36959074400723846, 0.6065094525663848, 0.14370437302298023, + 0.7471705670569604, 0.3991987649234662, 0.6377650280189965, 0.7966311569652879, 0.6699688376083757, + 0.11046232705364989, 0.0343797538976689, 0.4263281825639861, 0.06018487827135577, 0.27543446279089245, + 0.6307832714073219}, + {0.007401290917390502, 0.8640389112554114, 0.08779720334707941, 0.6587228816523323, 0.21985520800643954, + 0.4963969113562372, 0.25041837572771375, 0.2996763341721056}, + {0.9283706112012573, 0.9008656544411213, 0.01039871402142889, 0.637525445174257}, + {0.838047562953646, 0.9020667732919446}, + {0.7455441724472716, 0.5020772412030806, 0.10951344204683389, 0.30933274344662487}, + {0.8180656768575455, 0.8666279680604059, 0.42583580021283085, 0.1433193988956274, 0.8669332518159855, + 0.7414686722465978, 0.5939337651104747, 0.6789552509532184}, + {0.48808916431556837, 0.8241477106539906, 0.7262098103482119, 0.899834357237205, 0.8834046134657804, + 0.6029189784057519, 0.46118570354302213, 0.20904657865994403, 0.17528071980731652, 0.9766278629190644, + 0.2643652212919221, 0.22466069704047253, 0.5868640515110155, 0.40572150873462176, 0.012091970132293572, + 0.7702657347835162}, + {0.8296574984897637, 0.2009561060794306, 0.4599409596490742, 0.2981967624462212}, + {0.9517083930235997, 0.9818426792525043}}; - double[][][] outputData = { - { { 8.114375727757187, 0.5925913961852104, -0.8416569218183314, -0.22284951774343884, - -1.0066402879440184, 0.27554774792062964, -1.483807235895858, 0.5061247315739602, - 1.1727025144588428, 0.5061247315739602, -1.483807235895858, 0.27554774792062964, - -1.0066402879440184, -0.22284951774343884, -0.8416569218183314, 0.5925913961852104 }, - { 0.0, 0.00425759907320819, 0.5957854277534866, 2.186428829721094, -0.4223350357989528, - 1.3405434768573832, -0.14528152773100134, -0.14643207093697122, 0.0, - 0.14643207093697122, 0.14528152773100134, -1.3405434768573832, 0.4223350357989528, - -2.186428829721094, -0.5957854277534866, -0.00425759907320819 } }, - { { 7.9964043972637295, -0.85788941138668, 2.065867366646952, 0.2645073319462676, 0.759861880368734, - 0.6762306422958229, 1.2129304850342315, 0.09235810856712429, -0.46731515619431363, - 0.09235810856712429, 1.2129304850342315, 0.6762306422958229, 0.759861880368734, - 0.2645073319462676, 2.065867366646952, -0.85788941138668 }, - { 0.0, 0.8007124150209053, 1.0870614472850983, -1.0027520373092829, 0.7594021543648628, - -0.47928657559005056, -0.21335053145242822, 1.784731712940313, 0.0, -1.784731712940313, - 0.21335053145242822, 0.47928657559005056, -0.7594021543648628, 1.0027520373092829, - -1.0870614472850983, -0.8007124150209053 } }, - { { 4.8079102942811245, 1.156115577646565, 0.008713270105993765, -0.6005956154148545, - 1.0312859519915147, -0.6005956154148545, 0.008713270105993765, 1.156115577646565 }, - { 0.0, 0.17455045446816958, -0.2210210525614047, -0.5980611421691582, 0.0, 0.5980611421691582, - 0.2210210525614047, -0.17455045446816958 } }, - { { 1.9173888502627623, -0.18742000810282478, -0.3509921708779866, -0.18742000810282478 }, - { 0.0, 0.5792184919499044, 0.0, -0.5792184919499044 } }, - { { 1.0666649987824708, 0.20375348026926943 }, { 0.0, 0.0 } }, - { { 0.7766623394586858, 0.3468775187816395 }, { 0.0, 0.0 } }, - { { 7.35155981522581, 1.7384348144213493, -0.36205061306792863, 0.7115391307931807, -0.1506675500686654, - 0.4372769750045813, -0.7703890395558666, -0.27013162034836824, 0.5020650253531165, - -0.27013162034836824, -0.7703890395558666, 0.4372769750045813, -0.1506675500686654, - 0.7115391307931807, -0.36205061306792863, 1.7384348144213493 }, - { 0.0, 1.2350796850853472, 1.3528374749311034, -0.5177322782271997, -1.166738312165751, - -0.7694859455764832, -1.3882955152725982, 0.3917123754407248, 0.0, -0.3917123754407248, - 1.3882955152725982, 0.7694859455764832, 1.166738312165751, 0.5177322782271997, - -1.3528374749311034, -1.2350796850853472 } }, - { { 7.208291303963872, 1.6448982875247267, -0.8709621365176501, -1.2546616319678878, 1.1532801435016609, - 0.9134446382882846, -0.26503782507390033, 1.2172032274813867, 1.8305697339514122, - 1.2172032274813867, -0.26503782507390033, 0.9134446382882846, 1.1532801435016609, - -1.2546616319678878, -0.8709621365176501, 1.6448982875247267 }, - { 0.0, 0.7473959934264138, 0.7534008934360946, -0.07634193189820554, -0.07042978739273931, - -0.6293807074039233, 0.25979948108047013, -0.3847534714803642, 0.0, 0.3847534714803642, - -0.25979948108047013, 0.6293807074039233, 0.07042978739273931, 0.07634193189820554, - -0.7534008934360946, -0.7473959934264138 } }, - { { 2.3513441029956232, 0.5338613929301284, -0.4648304747063223, 0.5338613929301284 }, - { 0.0, 0.02120134741487134, 0.0, -0.02120134741487134 } }, - { { 5.823393482351781, 0.9136499207248283, -1.603500573737872, -0.7020472269919936, 0.13075774669727602, - -1.2520745980052537, -2.040383640336792, 1.3386481732883144, 2.1142453296794463, - 1.3386481732883144, -2.040383640336792, -1.2520745980052537, 0.13075774669727602, - -0.7020472269919936, -1.603500573737872, 0.9136499207248283 }, - { 0.0, -0.4121748237807462, -0.08693254435692731, -0.7861482143975829, 0.3065394610092609, - -0.7981716440636941, 0.19042850749330453, -0.35950616245668343, 0.0, - 0.35950616245668343, -0.19042850749330453, 0.7981716440636941, -0.3065394610092609, - 0.7861482143975829, 0.08693254435692731, 0.4121748237807462 } }, - { { 4.1482797409272285, 0.6066330243002018, -0.9871528935889964, -1.0659626805868174, - -0.7293673274793042, -1.0659626805868174, -0.9871528935889964, 0.6066330243002018 }, - { 0.0, -0.12559491018544305, 0.02769304968668118, 0.40390593762014965, 0.0, - -0.40390593762014965, -0.02769304968668118, 0.12559491018544305 } }, - { { 7.923806369209862, -1.0597881688755275, -0.15343866443479803, -0.701824885719718, - 0.5927359221917725, -0.40648735694530763, -1.620298961603096, -0.14398571120068893, - -0.2638022272358147, -0.14398571120068893, -1.620298961603096, -0.40648735694530763, - 0.5927359221917725, -0.701824885719718, -0.15343866443479803, -1.0597881688755275 }, - { 0.0, -0.5098005943518916, -0.3926881055602594, 0.7944914010859094, -0.9233176838083876, - -1.1214718762239086, 0.08439928489476206, -1.3548892080692885, 0.0, 1.3548892080692885, - -0.08439928489476206, 1.1214718762239086, 0.9233176838083876, -0.7944914010859094, - 0.3926881055602594, 0.5098005943518916 } }, - { { 7.9762380109034545, -0.08102561731855723, -0.2601631725122897, 1.2537848144634045, - 0.38617374907059565, -0.6434301753632343, -1.7188495230569896, -0.2064458730243935, - -2.53568503191929, -0.2064458730243935, -1.7188495230569896, -0.6434301753632343, - 0.38617374907059565, 1.2537848144634045, -0.2601631725122897, -0.08102561731855723 }, - { 0.0, -0.17500593841444329, -0.10397394228803791, 1.0518049218488463, 0.02701633202646292, - 0.05602413235726633, -1.0664029708997815, 0.0983345724678551, 0.0, -0.0983345724678551, - 1.0664029708997815, -0.05602413235726633, -0.02701633202646292, -1.0518049218488463, - 0.10397394228803791, 0.17500593841444329 } }, - { { 1.000937720477208, 0.6313784664456162 }, { 0.0, 0.0 } }, - { { 6.152408291718494, -0.701864450013982, 0.7389893051923667, -0.6570737426544427, -0.8244239353579019, - 0.6745758755808154, -0.6429889222091717, -1.3174954401270726, 0.5927066064261997, - -1.3174954401270726, -0.6429889222091717, 0.6745758755808154, -0.8244239353579019, - -0.6570737426544427, 0.7389893051923667, -0.701864450013982 }, - { 0.0, 0.2166164561807843, -0.37363526340890696, 1.9069743954629836, 0.47202833885420037, - 0.9505271452772353, -1.1862153000609332, -0.6152559835494934, 0.0, 0.6152559835494934, - 1.1862153000609332, -0.9505271452772353, -0.47202833885420037, -1.9069743954629836, - 0.37363526340890696, -0.2166164561807843 } }, - { { 6.9013391753724305, 0.6001789336263573, 1.5013874922440678, 0.21234328277453474, 1.5419271519293472, - -0.8364191839302667, 0.1618014829053418, 0.6390670818196226, 0.5291135045797226, - 0.6390670818196226, 0.1618014829053418, -0.8364191839302667, 1.5419271519293472, - 0.21234328277453474, 1.5013874922440678, 0.6001789336263573 }, - { 0.0, 1.402962398507333, -0.1657532563195791, 1.1827153358128617, -0.12153139536338342, - 1.0437298788884946, -0.5363130165129142, 1.2792578955791045, 0.0, -1.2792578955791045, - 0.5363130165129142, -1.0437298788884946, 0.12153139536338342, -1.1827153358128617, - 0.1657532563195791, -1.402962398507333 } }, - { { 1.1286681990682257, -0.027632155703683714 }, { 0.0, 0.0 } }, - { { 4.465440402428082, 0.5404896430462359, 1.0893508383258046, -0.3492095413061488, 0.1385553900636749, - -0.3492095413061488, 1.0893508383258046, 0.5404896430462359 }, - { 0.0, 0.48347502888031313, -0.7561287236420293, -0.15953071092284696, 0.0, 0.15953071092284696, - 0.7561287236420293, -0.48347502888031313 } }, - { { 2.18101701610746, 0.35351142968164073, 0.9854216124931725, 0.35351142968164073 }, - { 0.0, 0.15126932127998183, 0.0, -0.15126932127998183 } }, - { { 0.7730775228273274, 0.6403296280435042 }, { 0.0, 0.0 } }, - { { 3.85947096373431, 0.8014953943534662, -1.1740666585564816, 0.10954547527787173, 0.4334790012358889, - 0.10954547527787173, -1.1740666585564816, 0.8014953943534662 }, - { 0.0, -1.034586268721768, -0.2675790993905529, -0.5415844337354562, 0.0, 0.5415844337354562, - 0.2675790993905529, 1.034586268721768 } }, - { { 1.4961950885578212, -0.339042319649027 }, { 0.0, 0.0 } }, - { { 1.8626140316073916, 0.3955798034483067, -0.5572999963934553, 0.3955798034483067 }, - { 0.0, 0.7081202582269981, 0.0, -0.7081202582269981 } }, - { { 9.64323205203265, 1.0222697551724542, 1.0324747401969991, 0.77067558102107, -0.6590386688977952, - -1.496670252648384, -0.11638342471365593, 1.3716339455533146, 0.2213297936960057, - 1.3716339455533146, -0.11638342471365593, -1.496670252648384, -0.6590386688977952, - 0.77067558102107, 1.0324747401969991, 1.0222697551724542 }, - { 0.0, -0.634301326905181, 0.9994818327031652, -0.7420494450419806, 1.473577412873138, - -0.18504033224304028, -0.24256873993315792, 0.3857755742440071, 0.0, - -0.3857755742440071, 0.24256873993315792, 0.18504033224304028, -1.473577412873138, - 0.7420494450419806, -0.9994818327031652, 0.634301326905181 } }, - { { 3.8135263644772612, -0.07491474109162899, 1.35565242660513, 0.01806450671561144, 0.4183255282924616, - 0.01806450671561144, 1.35565242660513, -0.07491474109162899 }, - { 0.0, -0.1902080867353592, -0.5050999775869104, 0.012630346207943427, 0.0, - -0.012630346207943427, 0.5050999775869104, 0.1902080867353592 } }, - { { 8.382787568513443, -0.6219859812329366, 0.025775381293649605, -0.2422386710017057, - -0.06536820337234373, 1.9046978726738621, -0.09138364751214496, -0.23299784657463904, - 1.933719826653804, -0.23299784657463904, -0.09138364751214496, 1.9046978726738621, - -0.06536820337234373, -0.2422386710017057, 0.025775381293649605, -0.6219859812329366 }, - { 0.0, -0.4421693421141475, -0.8962692960095064, -0.27841993134928456, -0.23645673550524116, - -0.06789283541651772, 0.6269367662742193, 0.9325979087338476, 0.0, -0.9325979087338476, - -0.6269367662742193, 0.06789283541651772, 0.23645673550524116, 0.27841993134928456, - 0.8962692960095064, 0.4421693421141475 } }, - { { 7.835696127070186, -0.013838185418746013, 0.3035453412810106, 0.36108371712932497, - 0.098842082743269, -0.20157362393768677, -0.8327023741784841, 0.4381159566818401, - -0.8332596991032202, 0.4381159566818401, -0.8327023741784841, -0.20157362393768677, - 0.098842082743269, 0.36108371712932497, 0.3035453412810106, -0.013838185418746013 }, - { 0.0, 1.2951778169532924, 0.667179370978872, 0.6934919236313549, 0.07342250044148102, - 0.36890779027205756, -0.6829530755425919, 0.24588193212198017, 0.0, - -0.24588193212198017, 0.6829530755425919, -0.36890779027205756, -0.07342250044148102, - -0.6934919236313549, -0.667179370978872, -1.2951778169532924 } }, - { { 0.65181758852391, -0.2901879917359539 }, { 0.0, 0.0 } }, - { { 9.012668576685904, -1.3888872356007793, -0.4114927520171256, -0.937566206657709, 0.8033347365401919, - -0.7316542605934145, 0.2210854368582879, 0.09879206835957632, -0.807165681169038, - 0.09879206835957632, 0.2210854368582879, -0.7316542605934145, 0.8033347365401919, - -0.937566206657709, -0.4114927520171256, -1.3888872356007793 }, - { 0.0, -0.5973815143710736, -1.2682664037495632, 0.009556220704126317, 0.26514707020269235, - 0.29524978930704304, 1.1170830276155366, -1.1459346083740547, 0.0, 1.1459346083740547, - -1.1170830276155366, -0.29524978930704304, -0.26514707020269235, -0.009556220704126317, - 1.2682664037495632, 0.5973815143710736 } }, - { { 0.32006777232134676, -0.2379581888566329 }, { 0.0, 0.0 } }, - { { 4.292666505555655, -0.679815938802624, -0.02654913847102025, 0.47599689706754467, - -1.3302757727530472, 0.47599689706754467, -0.02654913847102025, -0.679815938802624 }, - { 0.0, 0.3262799322896599, -0.21111945382619868, -0.9202259326540763, 0.0, 0.9202259326540763, - 0.21111945382619868, -0.3262799322896599 } }, - { { 1.7350442475752934, -0.6233379571427505, -0.2797198423584627, -0.6233379571427505 }, - { 0.0, 0.6747501293329352, 0.0, -0.6747501293329352 } }, - { { 0.8132388790225079, 0.04811108187384383 }, { 0.0, 0.0 } }, - { { 2.875589166781041, 0.7713866408436004, -0.25319616163672876, 0.48388479260742556, - 0.2188818383177884, 0.48388479260742556, -0.25319616163672876, 0.7713866408436004 }, - { 0.0, 0.9013306732173781, 0.22958543602056936, -0.8326599800681568, 0.0, 0.8326599800681568, - -0.22958543602056936, -0.9013306732173781 } }, - { { 2.8525916869770724, 0.43974029342312404, 0.6712099156970159, 0.09521066897034558, - -0.539650435667614, 0.09521066897034558, 0.6712099156970159, 0.43974029342312404 }, - { 0.0, -0.49532500025494997, -0.6997585394717769, -0.266039902883742, 0.0, 0.266039902883742, - 0.6997585394717769, 0.49532500025494997 } }, - { { 1.036746121824228, -0.6804276956916634 }, { 0.0, 0.0 } }, - { { 1.240631468309218, -0.06628109462726572 }, { 0.0, 0.0 } }, - { { 4.229686697467945, 0.25348848112557965, 0.4344226843867871, -1.452105924231748, 0.46564470866787655, - -1.452105924231748, 0.4344226843867871, 0.25348848112557965 }, - { 0.0, 0.7117411383627559, -0.06327455381858982, -0.4341334890354749, 0.0, 0.4341334890354749, - 0.06327455381858982, -0.7117411383627559 } }, - { { 5.8544823042369405, -0.6738021106296501, 0.057924457678316, 0.3335982635286765, - -0.02179340589959944, 0.3335982635286765, 0.057924457678316, -0.6738021106296501 }, - { 0.0, 0.3174912848162346, -0.36943273787322806, -0.6702965937136942, 0.0, 0.6702965937136942, - 0.36943273787322806, -0.3174912848162346 } }, - { { 5.450612521005906, -0.9485093939451186, -0.2788520382370394, 0.07203392450448454, - 1.2995481768870563, 0.07203392450448454, -0.2788520382370394, -0.9485093939451186 }, - { 0.0, -0.4094516196808049, 0.304910404211417, -0.670270419054986, 0.0, 0.670270419054986, - -0.304910404211417, 0.4094516196808049 } }, - { { 3.4663616304408476, 0.249168612623289, -0.8292405827982222, 0.04438095241984823, - -1.2083820915944758, 0.04438095241984823, -0.8292405827982222, 0.249168612623289 }, - { 0.0, 0.09216717947344956, -0.6561835806299711, -0.12920893221652552, 0.0, 0.12920893221652552, - 0.6561835806299711, -0.09216717947344956 } }, - { { 6.797683010944343, -0.49676461769680663, 1.4010987748295984, -0.2858605784410965, - 0.8907344700681898, -0.14132138994562243, 0.4096125405094099, 0.4524501833871039, - -0.3974688757069753, 0.4524501833871039, 0.4096125405094099, -0.14132138994562243, - 0.8907344700681898, -0.2858605784410965, 1.4010987748295984, -0.49676461769680663 }, - { 0.0, -0.978354076595978, 0.5864810413074062, 0.3462260624882627, 0.22129906845508507, - 1.4529657578358368, 0.19732072800046563, -1.0021096194124273, 0.0, 1.0021096194124273, - -0.19732072800046563, -1.4529657578358368, -0.22129906845508507, -0.3462260624882627, - -0.5864810413074062, 0.978354076595978 } }, - { { 2.8843071164347096, -0.20637601439624492, -0.11095908015096312, -0.21853181978185315, - -1.753362960437463, -0.21853181978185315, -0.11095908015096312, -0.20637601439624492 }, - { 0.0, -0.35122522728194183, -0.40203660678721076, -0.6764675720432105, 0.0, 0.6764675720432105, - 0.40203660678721076, 0.35122522728194183 } }, - { { 2.4771604248380648, 0.9179718971798284, -0.5996217743926923, 0.9179718971798284 }, - { 0.0, -0.2633402092668643, 0.0, 0.2633402092668643 } }, - { { 1.7401143362455906, -0.0640192103382986 }, { 0.0, 0.0 } }, - { { 1.666467599143811, 0.6360307304004377, 0.04364762984439996, 0.6360307304004377 }, - { 0.0, -0.19274449775645575, 0.0, 0.19274449775645575 } }, - { { 5.1351397841526865, 0.41838515507659374, 0.6652293633502253, -0.5161203049934737, - 0.2743972038409872, -0.5161203049934737, 0.6652293633502253, 0.41838515507659374 }, - { 0.0, 0.4583487213357241, -0.7858219904581578, 0.1221527915404364, 0.0, -0.1221527915404364, - 0.7858219904581578, -0.4583487213357241 } }, - { { 8.510714682849697, 0.882363829984205, -0.34943439017669387, 0.018617453499083503, 0.669785843784231, - 0.588967002531606, -1.2643631715311279, -0.2387145079818872, -1.3157321740194359, - -0.2387145079818872, -1.2643631715311279, 0.588967002531606, 0.669785843784231, - 0.018617453499083503, -0.34943439017669387, 0.882363829984205 }, - { 0.0, -1.4735161576417974, -1.1800811497522834, 0.6456240585574142, -0.7056086929922909, - 1.3408042647661378, -0.14548643382264692, 0.40782629638598544, 0.0, - -0.40782629638598544, 0.14548643382264692, -1.3408042647661378, 0.7056086929922909, - -0.6456240585574142, 1.1800811497522834, 1.4735161576417974 } }, - { { 1.7887513266644897, 0.36971653884068945, 0.790445589613186, 0.36971653884068945 }, - { 0.0, 0.09724065636679058, 0.0, -0.09724065636679058 } }, - { { 1.933551072276104, -0.030134286228904683 }, { 0.0, 0.0 } } - }; + double[][][] outputData = { + {{8.114375727757187, 0.5925913961852104, -0.8416569218183314, -0.22284951774343884, -1.0066402879440184, + 0.27554774792062964, -1.483807235895858, 0.5061247315739602, 1.1727025144588428, 0.5061247315739602, + -1.483807235895858, 0.27554774792062964, -1.0066402879440184, -0.22284951774343884, -0.8416569218183314, + 0.5925913961852104}, + {0.0, 0.00425759907320819, 0.5957854277534866, 2.186428829721094, -0.4223350357989528, + 1.3405434768573832, -0.14528152773100134, -0.14643207093697122, 0.0, 0.14643207093697122, + 0.14528152773100134, -1.3405434768573832, 0.4223350357989528, -2.186428829721094, + -0.5957854277534866, -0.00425759907320819}}, + {{7.9964043972637295, -0.85788941138668, 2.065867366646952, 0.2645073319462676, 0.759861880368734, + 0.6762306422958229, 1.2129304850342315, 0.09235810856712429, -0.46731515619431363, 0.09235810856712429, + 1.2129304850342315, 0.6762306422958229, 0.759861880368734, 0.2645073319462676, 2.065867366646952, + -0.85788941138668}, + {0.0, 0.8007124150209053, 1.0870614472850983, -1.0027520373092829, 0.7594021543648628, + -0.47928657559005056, -0.21335053145242822, 1.784731712940313, 0.0, -1.784731712940313, + 0.21335053145242822, 0.47928657559005056, -0.7594021543648628, 1.0027520373092829, + -1.0870614472850983, -0.8007124150209053}}, + {{4.8079102942811245, 1.156115577646565, 0.008713270105993765, -0.6005956154148545, 1.0312859519915147, + -0.6005956154148545, 0.008713270105993765, 1.156115577646565}, + {0.0, 0.17455045446816958, -0.2210210525614047, -0.5980611421691582, 0.0, 0.5980611421691582, + 0.2210210525614047, -0.17455045446816958}}, + {{1.9173888502627623, -0.18742000810282478, -0.3509921708779866, -0.18742000810282478}, + {0.0, 0.5792184919499044, 0.0, -0.5792184919499044}}, + {{1.0666649987824708, 0.20375348026926943}, {0.0, 0.0}}, + {{0.7766623394586858, 0.3468775187816395}, {0.0, 0.0}}, + {{7.35155981522581, 1.7384348144213493, -0.36205061306792863, 0.7115391307931807, -0.1506675500686654, + 0.4372769750045813, -0.7703890395558666, -0.27013162034836824, 0.5020650253531165, -0.27013162034836824, + -0.7703890395558666, 0.4372769750045813, -0.1506675500686654, 0.7115391307931807, -0.36205061306792863, + 1.7384348144213493}, + {0.0, 1.2350796850853472, 1.3528374749311034, -0.5177322782271997, -1.166738312165751, + -0.7694859455764832, -1.3882955152725982, 0.3917123754407248, 0.0, -0.3917123754407248, + 1.3882955152725982, 0.7694859455764832, 1.166738312165751, 0.5177322782271997, -1.3528374749311034, + -1.2350796850853472}}, + {{7.208291303963872, 1.6448982875247267, -0.8709621365176501, -1.2546616319678878, 1.1532801435016609, + 0.9134446382882846, -0.26503782507390033, 1.2172032274813867, 1.8305697339514122, 1.2172032274813867, + -0.26503782507390033, 0.9134446382882846, 1.1532801435016609, -1.2546616319678878, -0.8709621365176501, + 1.6448982875247267}, + {0.0, 0.7473959934264138, 0.7534008934360946, -0.07634193189820554, -0.07042978739273931, + -0.6293807074039233, 0.25979948108047013, -0.3847534714803642, 0.0, 0.3847534714803642, + -0.25979948108047013, 0.6293807074039233, 0.07042978739273931, 0.07634193189820554, + -0.7534008934360946, -0.7473959934264138}}, + {{2.3513441029956232, 0.5338613929301284, -0.4648304747063223, 0.5338613929301284}, + {0.0, 0.02120134741487134, 0.0, -0.02120134741487134}}, + {{5.823393482351781, 0.9136499207248283, -1.603500573737872, -0.7020472269919936, 0.13075774669727602, + -1.2520745980052537, -2.040383640336792, 1.3386481732883144, 2.1142453296794463, 1.3386481732883144, + -2.040383640336792, -1.2520745980052537, 0.13075774669727602, -0.7020472269919936, -1.603500573737872, + 0.9136499207248283}, + {0.0, -0.4121748237807462, -0.08693254435692731, -0.7861482143975829, 0.3065394610092609, + -0.7981716440636941, 0.19042850749330453, -0.35950616245668343, 0.0, 0.35950616245668343, + -0.19042850749330453, 0.7981716440636941, -0.3065394610092609, 0.7861482143975829, + 0.08693254435692731, 0.4121748237807462}}, + {{4.1482797409272285, 0.6066330243002018, -0.9871528935889964, -1.0659626805868174, -0.7293673274793042, + -1.0659626805868174, -0.9871528935889964, 0.6066330243002018}, + {0.0, -0.12559491018544305, 0.02769304968668118, 0.40390593762014965, 0.0, -0.40390593762014965, + -0.02769304968668118, 0.12559491018544305}}, + {{7.923806369209862, -1.0597881688755275, -0.15343866443479803, -0.701824885719718, 0.5927359221917725, + -0.40648735694530763, -1.620298961603096, -0.14398571120068893, -0.2638022272358147, + -0.14398571120068893, -1.620298961603096, -0.40648735694530763, 0.5927359221917725, -0.701824885719718, + -0.15343866443479803, -1.0597881688755275}, + {0.0, -0.5098005943518916, -0.3926881055602594, 0.7944914010859094, -0.9233176838083876, + -1.1214718762239086, 0.08439928489476206, -1.3548892080692885, 0.0, 1.3548892080692885, + -0.08439928489476206, 1.1214718762239086, 0.9233176838083876, -0.7944914010859094, + 0.3926881055602594, 0.5098005943518916}}, + {{7.9762380109034545, -0.08102561731855723, -0.2601631725122897, 1.2537848144634045, 0.38617374907059565, + -0.6434301753632343, -1.7188495230569896, -0.2064458730243935, -2.53568503191929, -0.2064458730243935, + -1.7188495230569896, -0.6434301753632343, 0.38617374907059565, 1.2537848144634045, -0.2601631725122897, + -0.08102561731855723}, + {0.0, -0.17500593841444329, -0.10397394228803791, 1.0518049218488463, 0.02701633202646292, + 0.05602413235726633, -1.0664029708997815, 0.0983345724678551, 0.0, -0.0983345724678551, + 1.0664029708997815, -0.05602413235726633, -0.02701633202646292, -1.0518049218488463, + 0.10397394228803791, 0.17500593841444329}}, + {{1.000937720477208, 0.6313784664456162}, {0.0, 0.0}}, + {{6.152408291718494, -0.701864450013982, 0.7389893051923667, -0.6570737426544427, -0.8244239353579019, + 0.6745758755808154, -0.6429889222091717, -1.3174954401270726, 0.5927066064261997, -1.3174954401270726, + -0.6429889222091717, 0.6745758755808154, -0.8244239353579019, -0.6570737426544427, 0.7389893051923667, + -0.701864450013982}, + {0.0, 0.2166164561807843, -0.37363526340890696, 1.9069743954629836, 0.47202833885420037, + 0.9505271452772353, -1.1862153000609332, -0.6152559835494934, 0.0, 0.6152559835494934, + 1.1862153000609332, -0.9505271452772353, -0.47202833885420037, -1.9069743954629836, + 0.37363526340890696, -0.2166164561807843}}, + {{6.9013391753724305, 0.6001789336263573, 1.5013874922440678, 0.21234328277453474, 1.5419271519293472, + -0.8364191839302667, 0.1618014829053418, 0.6390670818196226, 0.5291135045797226, 0.6390670818196226, + 0.1618014829053418, -0.8364191839302667, 1.5419271519293472, 0.21234328277453474, 1.5013874922440678, + 0.6001789336263573}, + {0.0, 1.402962398507333, -0.1657532563195791, 1.1827153358128617, -0.12153139536338342, + 1.0437298788884946, -0.5363130165129142, 1.2792578955791045, 0.0, -1.2792578955791045, + 0.5363130165129142, -1.0437298788884946, 0.12153139536338342, -1.1827153358128617, + 0.1657532563195791, -1.402962398507333}}, + {{1.1286681990682257, -0.027632155703683714}, {0.0, 0.0}}, + {{4.465440402428082, 0.5404896430462359, 1.0893508383258046, -0.3492095413061488, 0.1385553900636749, + -0.3492095413061488, 1.0893508383258046, 0.5404896430462359}, + {0.0, 0.48347502888031313, -0.7561287236420293, -0.15953071092284696, 0.0, 0.15953071092284696, + 0.7561287236420293, -0.48347502888031313}}, + {{2.18101701610746, 0.35351142968164073, 0.9854216124931725, 0.35351142968164073}, + {0.0, 0.15126932127998183, 0.0, -0.15126932127998183}}, + {{0.7730775228273274, 0.6403296280435042}, {0.0, 0.0}}, + {{3.85947096373431, 0.8014953943534662, -1.1740666585564816, 0.10954547527787173, 0.4334790012358889, + 0.10954547527787173, -1.1740666585564816, 0.8014953943534662}, + {0.0, -1.034586268721768, -0.2675790993905529, -0.5415844337354562, 0.0, 0.5415844337354562, + 0.2675790993905529, 1.034586268721768}}, + {{1.4961950885578212, -0.339042319649027}, {0.0, 0.0}}, + {{1.8626140316073916, 0.3955798034483067, -0.5572999963934553, 0.3955798034483067}, + {0.0, 0.7081202582269981, 0.0, -0.7081202582269981}}, + {{9.64323205203265, 1.0222697551724542, 1.0324747401969991, 0.77067558102107, -0.6590386688977952, + -1.496670252648384, -0.11638342471365593, 1.3716339455533146, 0.2213297936960057, 1.3716339455533146, + -0.11638342471365593, -1.496670252648384, -0.6590386688977952, 0.77067558102107, 1.0324747401969991, + 1.0222697551724542}, + {0.0, -0.634301326905181, 0.9994818327031652, -0.7420494450419806, 1.473577412873138, + -0.18504033224304028, -0.24256873993315792, 0.3857755742440071, 0.0, -0.3857755742440071, + 0.24256873993315792, 0.18504033224304028, -1.473577412873138, 0.7420494450419806, + -0.9994818327031652, 0.634301326905181}}, + {{3.8135263644772612, -0.07491474109162899, 1.35565242660513, 0.01806450671561144, 0.4183255282924616, + 0.01806450671561144, 1.35565242660513, -0.07491474109162899}, + {0.0, -0.1902080867353592, -0.5050999775869104, 0.012630346207943427, 0.0, -0.012630346207943427, + 0.5050999775869104, 0.1902080867353592}}, + {{8.382787568513443, -0.6219859812329366, 0.025775381293649605, -0.2422386710017057, -0.06536820337234373, + 1.9046978726738621, -0.09138364751214496, -0.23299784657463904, 1.933719826653804, -0.23299784657463904, + -0.09138364751214496, 1.9046978726738621, -0.06536820337234373, -0.2422386710017057, + 0.025775381293649605, -0.6219859812329366}, + {0.0, -0.4421693421141475, -0.8962692960095064, -0.27841993134928456, -0.23645673550524116, + -0.06789283541651772, 0.6269367662742193, 0.9325979087338476, 0.0, -0.9325979087338476, + -0.6269367662742193, 0.06789283541651772, 0.23645673550524116, 0.27841993134928456, + 0.8962692960095064, 0.4421693421141475}}, + {{7.835696127070186, -0.013838185418746013, 0.3035453412810106, 0.36108371712932497, 0.098842082743269, + -0.20157362393768677, -0.8327023741784841, 0.4381159566818401, -0.8332596991032202, 0.4381159566818401, + -0.8327023741784841, -0.20157362393768677, 0.098842082743269, 0.36108371712932497, 0.3035453412810106, + -0.013838185418746013}, + {0.0, 1.2951778169532924, 0.667179370978872, 0.6934919236313549, 0.07342250044148102, + 0.36890779027205756, -0.6829530755425919, 0.24588193212198017, 0.0, -0.24588193212198017, + 0.6829530755425919, -0.36890779027205756, -0.07342250044148102, -0.6934919236313549, + -0.667179370978872, -1.2951778169532924}}, + {{0.65181758852391, -0.2901879917359539}, {0.0, 0.0}}, + {{9.012668576685904, -1.3888872356007793, -0.4114927520171256, -0.937566206657709, 0.8033347365401919, + -0.7316542605934145, 0.2210854368582879, 0.09879206835957632, -0.807165681169038, 0.09879206835957632, + 0.2210854368582879, -0.7316542605934145, 0.8033347365401919, -0.937566206657709, -0.4114927520171256, + -1.3888872356007793}, + {0.0, -0.5973815143710736, -1.2682664037495632, 0.009556220704126317, 0.26514707020269235, + 0.29524978930704304, 1.1170830276155366, -1.1459346083740547, 0.0, 1.1459346083740547, + -1.1170830276155366, -0.29524978930704304, -0.26514707020269235, -0.009556220704126317, + 1.2682664037495632, 0.5973815143710736}}, + {{0.32006777232134676, -0.2379581888566329}, {0.0, 0.0}}, + {{4.292666505555655, -0.679815938802624, -0.02654913847102025, 0.47599689706754467, -1.3302757727530472, + 0.47599689706754467, -0.02654913847102025, -0.679815938802624}, + {0.0, 0.3262799322896599, -0.21111945382619868, -0.9202259326540763, 0.0, 0.9202259326540763, + 0.21111945382619868, -0.3262799322896599}}, + {{1.7350442475752934, -0.6233379571427505, -0.2797198423584627, -0.6233379571427505}, + {0.0, 0.6747501293329352, 0.0, -0.6747501293329352}}, + {{0.8132388790225079, 0.04811108187384383}, {0.0, 0.0}}, + {{2.875589166781041, 0.7713866408436004, -0.25319616163672876, 0.48388479260742556, 0.2188818383177884, + 0.48388479260742556, -0.25319616163672876, 0.7713866408436004}, + {0.0, 0.9013306732173781, 0.22958543602056936, -0.8326599800681568, 0.0, 0.8326599800681568, + -0.22958543602056936, -0.9013306732173781}}, + {{2.8525916869770724, 0.43974029342312404, 0.6712099156970159, 0.09521066897034558, -0.539650435667614, + 0.09521066897034558, 0.6712099156970159, 0.43974029342312404}, + {0.0, -0.49532500025494997, -0.6997585394717769, -0.266039902883742, 0.0, 0.266039902883742, + 0.6997585394717769, 0.49532500025494997}}, + {{1.036746121824228, -0.6804276956916634}, {0.0, 0.0}}, + {{1.240631468309218, -0.06628109462726572}, {0.0, 0.0}}, + {{4.229686697467945, 0.25348848112557965, 0.4344226843867871, -1.452105924231748, 0.46564470866787655, + -1.452105924231748, 0.4344226843867871, 0.25348848112557965}, + {0.0, 0.7117411383627559, -0.06327455381858982, -0.4341334890354749, 0.0, 0.4341334890354749, + 0.06327455381858982, -0.7117411383627559}}, + {{5.8544823042369405, -0.6738021106296501, 0.057924457678316, 0.3335982635286765, -0.02179340589959944, + 0.3335982635286765, 0.057924457678316, -0.6738021106296501}, + {0.0, 0.3174912848162346, -0.36943273787322806, -0.6702965937136942, 0.0, 0.6702965937136942, + 0.36943273787322806, -0.3174912848162346}}, + {{5.450612521005906, -0.9485093939451186, -0.2788520382370394, 0.07203392450448454, 1.2995481768870563, + 0.07203392450448454, -0.2788520382370394, -0.9485093939451186}, + {0.0, -0.4094516196808049, 0.304910404211417, -0.670270419054986, 0.0, 0.670270419054986, + -0.304910404211417, 0.4094516196808049}}, + {{3.4663616304408476, 0.249168612623289, -0.8292405827982222, 0.04438095241984823, -1.2083820915944758, + 0.04438095241984823, -0.8292405827982222, 0.249168612623289}, + {0.0, 0.09216717947344956, -0.6561835806299711, -0.12920893221652552, 0.0, 0.12920893221652552, + 0.6561835806299711, -0.09216717947344956}}, + {{6.797683010944343, -0.49676461769680663, 1.4010987748295984, -0.2858605784410965, 0.8907344700681898, + -0.14132138994562243, 0.4096125405094099, 0.4524501833871039, -0.3974688757069753, 0.4524501833871039, + 0.4096125405094099, -0.14132138994562243, 0.8907344700681898, -0.2858605784410965, 1.4010987748295984, + -0.49676461769680663}, + {0.0, -0.978354076595978, 0.5864810413074062, 0.3462260624882627, 0.22129906845508507, + 1.4529657578358368, 0.19732072800046563, -1.0021096194124273, 0.0, 1.0021096194124273, + -0.19732072800046563, -1.4529657578358368, -0.22129906845508507, -0.3462260624882627, + -0.5864810413074062, 0.978354076595978}}, + {{2.8843071164347096, -0.20637601439624492, -0.11095908015096312, -0.21853181978185315, -1.753362960437463, + -0.21853181978185315, -0.11095908015096312, -0.20637601439624492}, + {0.0, -0.35122522728194183, -0.40203660678721076, -0.6764675720432105, 0.0, 0.6764675720432105, + 0.40203660678721076, 0.35122522728194183}}, + {{2.4771604248380648, 0.9179718971798284, -0.5996217743926923, 0.9179718971798284}, + {0.0, -0.2633402092668643, 0.0, 0.2633402092668643}}, + {{1.7401143362455906, -0.0640192103382986}, {0.0, 0.0}}, + {{1.666467599143811, 0.6360307304004377, 0.04364762984439996, 0.6360307304004377}, + {0.0, -0.19274449775645575, 0.0, 0.19274449775645575}}, + {{5.1351397841526865, 0.41838515507659374, 0.6652293633502253, -0.5161203049934737, 0.2743972038409872, + -0.5161203049934737, 0.6652293633502253, 0.41838515507659374}, + {0.0, 0.4583487213357241, -0.7858219904581578, 0.1221527915404364, 0.0, -0.1221527915404364, + 0.7858219904581578, -0.4583487213357241}}, + {{8.510714682849697, 0.882363829984205, -0.34943439017669387, 0.018617453499083503, 0.669785843784231, + 0.588967002531606, -1.2643631715311279, -0.2387145079818872, -1.3157321740194359, -0.2387145079818872, + -1.2643631715311279, 0.588967002531606, 0.669785843784231, 0.018617453499083503, -0.34943439017669387, + 0.882363829984205}, + {0.0, -1.4735161576417974, -1.1800811497522834, 0.6456240585574142, -0.7056086929922909, + 1.3408042647661378, -0.14548643382264692, 0.40782629638598544, 0.0, -0.40782629638598544, + 0.14548643382264692, -1.3408042647661378, 0.7056086929922909, -0.6456240585574142, + 1.1800811497522834, 1.4735161576417974}}, + {{1.7887513266644897, 0.36971653884068945, 0.790445589613186, 0.36971653884068945}, + {0.0, 0.09724065636679058, 0.0, -0.09724065636679058}}, + {{1.933551072276104, -0.030134286228904683}, {0.0, 0.0}}}; - long totalTime = 0; // Total time for all FFT computations - int numCalculations = 0; // Number of FFT computations + long totalTime = 0; // Total time for all FFT computations + int numCalculations = 0; // Number of FFT computations - for (int i = 0; i < inputData.length; i++) { - double[] re = inputData[i]; - double[] im = new double[re.length]; // Initialize with zeros + for(int i = 0; i < inputData.length; i++) { + double[] re = inputData[i]; + double[] im = new double[re.length]; // Initialize with zeros - double[][] expected = outputData[i]; + double[][] expected = outputData[i]; - long startTime = System.nanoTime(); - fft(re, im, 1, re.length); - long endTime = System.nanoTime(); + long startTime = System.nanoTime(); + fft(re, im, 1, re.length); + long endTime = System.nanoTime(); - totalTime += (endTime - startTime); - numCalculations++; + totalTime += (endTime - startTime); + numCalculations++; - double[][] actual = { re, im }; + double[][] actual = {re, im}; - // Validate the FFT results - validateFftResults(expected, actual, i + 1); - } + // Validate the FFT results + validateFftResults(expected, actual, i + 1); + } - double averageTime = (totalTime / 1e6) / numCalculations; // Average time in milliseconds - System.out.println("Average execution time: " + String.format("%.8f", averageTime / 1000) + " s"); - } + double averageTime = (totalTime / 1e6) / numCalculations; // Average time in milliseconds + System.out.println("Average execution time: " + String.format("%.8f", averageTime / 1000) + " s"); + } - private void validateFftResults(double[][] expected, double[][] actual, int lineNumber) { - int length = expected[0].length; - for (int i = 0; i < length; i++) { - assertEquals("Mismatch in real part at index " + i + " in line " + lineNumber, - expected[0][i], actual[0][i], 1e-9); - assertEquals("Mismatch in imaginary part at index " + i + " in line " + lineNumber, - expected[1][i], actual[1][i], 1e-9); - } - } + private void validateFftResults(double[][] expected, double[][] actual, int lineNumber) { + int length = expected[0].length; + for(int i = 0; i < length; i++) { + assertEquals("Mismatch in real part at index " + i + " in line " + lineNumber, expected[0][i], actual[0][i], + 1e-9); + assertEquals("Mismatch in imaginary part at index " + i + " in line " + lineNumber, expected[1][i], + actual[1][i], 1e-9); + } + } - @Test - public void testIfftWithRealNumpyData() { + @Test + public void testIfftWithRealNumpyData() { - // Define the input data array (real IFFT inputs) - double[][] inputData = { - { 0.5398705320215192, 0.1793355360736929, 0.9065254044489506, 0.45004385530909075, 0.11128090341119468, - 0.11742862805303522, 0.7574827475752481, 0.2778193170985158, 0.9251562928110273, - 0.9414429667551927, 0.45131569795507087, 0.9522067687409731, 0.22491032260636257, - 0.6579426733967295, 0.7021558730366062, 0.7861117825617701, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0 }, - { 0.6557180567930387, 0.7738395851954255, 0.5282681528342636, 0.6867068152297491, 0.032829567791427205, - 0.060095237489160236, 0.5250841288480065, 0.9356398294818203, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0.15785491881056868, 0.9056824935624028, 0.8375304868692421, 0.9188982121338262, 0, 0, 0, 0 }, - { 0.2362087514654716, 0.031154253034164303, 0.6157257211463447, 0.3773680629310894, 0, 0, 0, 0 }, - { 0.3751160998031162, 0.4253486725063852, 0.3535750705572219, 0.9557962271336204, 0, 0, 0, 0 }, - { 0.38741575855731003, 0.9967628087038197, 0.6755232743231949, 0.381124935406706, 0, 0, 0, 0 }, - { 0.7952407554893315, 0.5188862746399061, 0.4828070990424008, 0.41146079690881665, 0.5523554474649665, - 0.3371675982794765, 0.9086513990497248, 0.9480377757565142, 0.29262673918949955, - 0.5029584519842616, 0.1673523322593311, 0.909533552169132, 0.15086390797253646, - 0.08903568696591746, 0.9762713649580028, 0.79327533892987, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0 }, - { 0.16166982209302416, 0.48331281957071315, 0.8297038690562623, 0.2481405411628217, 0.37652293557756644, - 0.35568361617736255, 0.9886708014513632, 0.029556032119152365, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0.822115700110782, 0.29037305812947556, 0, 0 }, - { 0.1364777724494669, 0.4413020994649117, 0, 0 }, - { 0.4260028251864256, 0.2655565566693795, 0, 0 }, - { 0.8162743187254883, 0.6131080055615629, 0.016530179658639677, 0.3647951810730393, 0, 0, 0, 0 }, - { 0.32404367744317786, 0.9975863008660675, 0, 0 }, - { 0.9287511285137915, 0.9885317245671525, 0.29542406948511, 0.307667914659569, 0, 0, 0, 0 }, - { 0.6391321634628706, 0.8633641189940532, 0.4309541791982252, 0.9557087449945955, 0.3185873920535448, - 0.16533643465056203, 0.6461385746878021, 0.7043234939129138, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0.9801750501829359, 0.25789696071187607, 0, 0 }, - { 0.22972137572909201, 0.5817904901405937, 0, 0 }, - { 0.7239381644147941, 0.7687362814620392, 0.0873707955450509, 0.9506567262739939, 0, 0, 0, 0 }, - { 0.6141463720447145, 0.7403205748478789, 0.44227773948538585, 0.7277081432710838, 0.9352081899539255, - 0.4240443645856953, 0.4346954941173261, 0.5879657145765288, 0.23766574473359614, - 0.6530152865673962, 0.11484140648508312, 0.14574807816162283, 0.9624874452393961, - 0.5592097861022578, 0.005521469527721035, 0.06956740990618893, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0 }, - { 0.3838845675941237, 0.5088807724736699, 0, 0 }, - { 0.6983109361802508, 0.6244339329560505, 0.9008871130564603, 0.8932736022416019, 0.8727581351265782, - 0.9856438893255001, 0.5696671959058417, 0.15020089983612606, 0.058348906817813906, - 0.7629908745245777, 0.48442101642797164, 0.8502536763232881, 0.14290448979940018, - 0.14311515061344016, 0.933512771559466, 0.7512746845582662, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0 }, - { 0.7556811638466671, 0.7893033739069542, 0.3459619060619262, 0.19402945604163735, 0, 0, 0, 0 }, - { 0.14095847557513064, 0.9354102907913319, 0.47692666603219047, 0.9655253211517809, 0, 0, 0, 0 }, - { 0.5132699837151409, 0.48338858084587877, 0.21082789930932544, 0.38487660081876207, - 0.49257322976621676, 0.4546677266078989, 0.8125433122155187, 0.1871279228630126, - 0.9935603064559684, 0.14244380671166657, 0.33692793538062416, 0.8184163712132382, - 0.1909607709342982, 0.40792019329451423, 0.745094091124709, 0.5706291972106916, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0.13418006969295326, 0.4945716623889531, 0.11760243909921375, 0.2570864674258849, 0.07735057384397426, - 0.019114456711389338, 0.9417433018717989, 0.9498903789723119, 0.15850190266259456, - 0.2328071188590526, 0.25865880162909693, 0.008950504888770583, 0.8898672604984333, - 0.6296228316964232, 0.769087346766234, 0.595502278059397, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0 }, - { 0.6547029168282242, 0.4498064503056928, 0.5566645171023767, 0.8447764906827644, 0.8134333284372349, - 0.033684622580624546, 0.2968711640065672, 0.8285143785250315, 0.5791098493622432, - 0.5979371998171618, 0.3650614808824525, 0.8518268346228536, 0.8198089679249101, - 0.9819781459749414, 0.9725187053011243, 0.17296496382033655, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0 }, - { 0.6448779915504477, 0.09112098044117456, 0.9263097426435519, 0.27812390544808974, 0.2760810901241125, - 0.7646817729525881, 0.911491047258462, 0.4005509961726136, 0.9694912938404336, - 0.8886893035007664, 0.6133994082666177, 0.3533649879581323, 0.24573475689499646, - 0.8300954022435542, 0.010790385307265948, 0.3607772174510355, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0 }, - { 0.9753223707638033, 0.6956234223864403, 0.5023331089283625, 0.6507908760350136, 0.10653272730910857, - 0.4996211399988053, 0.8760128351901267, 0.602426660576998, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0.6504577559004796, 0.25145420305800725, 0.493505599054606, 0.4515689832358094, 0.14356101422612022, - 0.3341029115381504, 0.5008369992482713, 0.25927336770416753, 0.9144019406085643, - 0.269776237000278, 0.6382189705979336, 0.9554129674543379, 0.6887405590404585, - 0.5039371147315215, 0.8252078215260031, 0.10805307033951939, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0 }, - { 0.41724967962818993, 0.2781633024698039, 0, 0 }, - { 0.4002455779217782, 0.564838604931205, 0.1953327862932963, 0.7012116008559239, 0.8022033779885992, - 0.2808573734656151, 0.3000946668377774, 0.17230898600893108, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0.6461124772516227, 0.48674094781923605, 0.10036448207277027, 0.015471823700216714, - 0.7689797229739221, 0.03476124705509731, 0.8330787816937497, 0.35390071912245424, - 0.18458690293633118, 0.42355207544691875, 0.5798363871357116, 0.4243527922467508, - 0.2879869168805991, 0.13736279699966614, 0.26166696186760385, 0.9787373283247385, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0.1398260006661618, 0.12153200522616103, 0.7121966216410146, 0.9133849754561364, 0.7314612511926722, - 0.739267240037694, 0.7546027553155675, 0.342420614369761, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0.22574862111952854, 0.5690849507265368, 0.631317280107177, 0.8629733195605777, 0.3899681673812473, - 0.8794702418181954, 0.1517842815507917, 0.1657877194968821, 0.8083699897670112, - 0.4355578268421948, 0.1969236012819674, 0.06184299476225663, 0.07860704151155451, - 0.397292331637211, 0.3839838104251525, 0.26205058162224937, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0 }, - { 0.33135924721579535, 0.5366302683143123, 0.25432093749208906, 0.7498876975281418, 0, 0, 0, 0 }, - { 0.6134308716246728, 0.09706486959071292, 0.356584692623391, 0.9766274230749799, 0.24172086288049444, - 0.7974589582847552, 0.7896787540010787, 0.5664999509048404, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0.18439523648303413, 0.5938145715696269, 0.5138768707589609, 0.03744323769215385, 0.9335962783171388, - 0.5763983101382048, 0.2806801388811724, 0.40858218250589795, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0.6665104076212911, 0.12712939621992436, 0, 0 }, - { 0.07848088184489588, 0.9037683971776713, 0, 0 }, - { 0.2961816890284832, 0.6548352902037954, 0.004297273252101497, 0.6379597126860397, 0.394953301386181, - 0.3591579315172063, 0.5200829702509112, 0.7348916802637337, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0.15333899851758237, 0.9557388547562318, 0, 0 }, - { 0.24490652495244614, 0.764767557933974, 0.7412612945684217, 0.8345076232268165, 0.9713064054627377, - 0.1666699320547278, 0.6385013568574381, 0.02837645523199317, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0.43636393937714724, 0.5006457049751213, 0.29183886078443877, 0.7771528617739111, 0.9760896495636339, - 0.4211692751284558, 0.46136604887195976, 0.4385215837142291, 0.6480247707507675, - 0.2869034885339521, 0.21139535714638436, 0.623852932281303, 0.10723939013625783, - 0.7423978700202447, 0.8339592164587588, 0.6541139999860581, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0 }, - { 0.673783082139485, 0.08489915452638419, 0.4428704894374401, 0.6322989775504414, 0, 0, 0, 0 }, - { 0.435204198844909, 0.60467095443427, 0.43492988438024205, 0.45115625691681205, 0.6073353909030185, - 0.7354160659126683, 0.6584642868836427, 0.49841087464599554, 0.3218054140407358, - 0.8547287810979488, 0.25920067933895863, 0.3577532915633288, 0.17696516490870162, - 0.9810705645133889, 0.08037125000861489, 0.9406431999485564, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0 }, - { 0.14276827960992966, 0.13654559645650455, 0, 0 }, - { 0.10449615216668384, 0.8809636163201752, 0.23757224642400443, 0.6033844272609862, 0.3180497932853712, - 0.8000641846428788, 0.36691139380180937, 0.9936551508162524, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0.5619461607033829, 0.05587776081117257, 0.8242650361208828, 0.31679799606054226, 0.67922806899631, - 0.31896202698295617, 0.47844249679108297, 0.43194377662374206, 0, 0, 0, 0, 0, 0, 0, 0 }, - { 0.5928888719853808, 0.8700201519901183, 0.909292241027492, 0.1017088272754374, 0.7250571447087572, - 0.3172141314203841, 0.05139597560789155, 0.8469852045420578, 0.744706896124228, - 0.24878503090173887, 0.3248378441207508, 0.6159251794660624, 0.46869738424898, - 0.42094147190658915, 0.2078392223975123, 0.17713383848172415, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0 }, - { 0.8259324464059233, 0.9375338155913502, 0.5557276593014934, 0.017649722866079798, 0, 0, 0, 0 } - }; + // Define the input data array (real IFFT inputs) + double[][] inputData = { + {0.5398705320215192, 0.1793355360736929, 0.9065254044489506, 0.45004385530909075, 0.11128090341119468, + 0.11742862805303522, 0.7574827475752481, 0.2778193170985158, 0.9251562928110273, 0.9414429667551927, + 0.45131569795507087, 0.9522067687409731, 0.22491032260636257, 0.6579426733967295, 0.7021558730366062, + 0.7861117825617701, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0.6557180567930387, 0.7738395851954255, 0.5282681528342636, 0.6867068152297491, 0.032829567791427205, + 0.060095237489160236, 0.5250841288480065, 0.9356398294818203, 0, 0, 0, 0, 0, 0, 0, 0}, + {0.15785491881056868, 0.9056824935624028, 0.8375304868692421, 0.9188982121338262, 0, 0, 0, 0}, + {0.2362087514654716, 0.031154253034164303, 0.6157257211463447, 0.3773680629310894, 0, 0, 0, 0}, + {0.3751160998031162, 0.4253486725063852, 0.3535750705572219, 0.9557962271336204, 0, 0, 0, 0}, + {0.38741575855731003, 0.9967628087038197, 0.6755232743231949, 0.381124935406706, 0, 0, 0, 0}, + {0.7952407554893315, 0.5188862746399061, 0.4828070990424008, 0.41146079690881665, 0.5523554474649665, + 0.3371675982794765, 0.9086513990497248, 0.9480377757565142, 0.29262673918949955, 0.5029584519842616, + 0.1673523322593311, 0.909533552169132, 0.15086390797253646, 0.08903568696591746, 0.9762713649580028, + 0.79327533892987, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0.16166982209302416, 0.48331281957071315, 0.8297038690562623, 0.2481405411628217, 0.37652293557756644, + 0.35568361617736255, 0.9886708014513632, 0.029556032119152365, 0, 0, 0, 0, 0, 0, 0, 0}, + {0.822115700110782, 0.29037305812947556, 0, 0}, {0.1364777724494669, 0.4413020994649117, 0, 0}, + {0.4260028251864256, 0.2655565566693795, 0, 0}, + {0.8162743187254883, 0.6131080055615629, 0.016530179658639677, 0.3647951810730393, 0, 0, 0, 0}, + {0.32404367744317786, 0.9975863008660675, 0, 0}, + {0.9287511285137915, 0.9885317245671525, 0.29542406948511, 0.307667914659569, 0, 0, 0, 0}, + {0.6391321634628706, 0.8633641189940532, 0.4309541791982252, 0.9557087449945955, 0.3185873920535448, + 0.16533643465056203, 0.6461385746878021, 0.7043234939129138, 0, 0, 0, 0, 0, 0, 0, 0}, + {0.9801750501829359, 0.25789696071187607, 0, 0}, {0.22972137572909201, 0.5817904901405937, 0, 0}, + {0.7239381644147941, 0.7687362814620392, 0.0873707955450509, 0.9506567262739939, 0, 0, 0, 0}, + {0.6141463720447145, 0.7403205748478789, 0.44227773948538585, 0.7277081432710838, 0.9352081899539255, + 0.4240443645856953, 0.4346954941173261, 0.5879657145765288, 0.23766574473359614, 0.6530152865673962, + 0.11484140648508312, 0.14574807816162283, 0.9624874452393961, 0.5592097861022578, 0.005521469527721035, + 0.06956740990618893, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0.3838845675941237, 0.5088807724736699, 0, 0}, + {0.6983109361802508, 0.6244339329560505, 0.9008871130564603, 0.8932736022416019, 0.8727581351265782, + 0.9856438893255001, 0.5696671959058417, 0.15020089983612606, 0.058348906817813906, 0.7629908745245777, + 0.48442101642797164, 0.8502536763232881, 0.14290448979940018, 0.14311515061344016, 0.933512771559466, + 0.7512746845582662, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0.7556811638466671, 0.7893033739069542, 0.3459619060619262, 0.19402945604163735, 0, 0, 0, 0}, + {0.14095847557513064, 0.9354102907913319, 0.47692666603219047, 0.9655253211517809, 0, 0, 0, 0}, + {0.5132699837151409, 0.48338858084587877, 0.21082789930932544, 0.38487660081876207, 0.49257322976621676, + 0.4546677266078989, 0.8125433122155187, 0.1871279228630126, 0.9935603064559684, 0.14244380671166657, + 0.33692793538062416, 0.8184163712132382, 0.1909607709342982, 0.40792019329451423, 0.745094091124709, + 0.5706291972106916, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0.13418006969295326, 0.4945716623889531, 0.11760243909921375, 0.2570864674258849, 0.07735057384397426, + 0.019114456711389338, 0.9417433018717989, 0.9498903789723119, 0.15850190266259456, 0.2328071188590526, + 0.25865880162909693, 0.008950504888770583, 0.8898672604984333, 0.6296228316964232, 0.769087346766234, + 0.595502278059397, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0.6547029168282242, 0.4498064503056928, 0.5566645171023767, 0.8447764906827644, 0.8134333284372349, + 0.033684622580624546, 0.2968711640065672, 0.8285143785250315, 0.5791098493622432, 0.5979371998171618, + 0.3650614808824525, 0.8518268346228536, 0.8198089679249101, 0.9819781459749414, 0.9725187053011243, + 0.17296496382033655, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0.6448779915504477, 0.09112098044117456, 0.9263097426435519, 0.27812390544808974, 0.2760810901241125, + 0.7646817729525881, 0.911491047258462, 0.4005509961726136, 0.9694912938404336, 0.8886893035007664, + 0.6133994082666177, 0.3533649879581323, 0.24573475689499646, 0.8300954022435542, 0.010790385307265948, + 0.3607772174510355, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0.9753223707638033, 0.6956234223864403, 0.5023331089283625, 0.6507908760350136, 0.10653272730910857, + 0.4996211399988053, 0.8760128351901267, 0.602426660576998, 0, 0, 0, 0, 0, 0, 0, 0}, + {0.6504577559004796, 0.25145420305800725, 0.493505599054606, 0.4515689832358094, 0.14356101422612022, + 0.3341029115381504, 0.5008369992482713, 0.25927336770416753, 0.9144019406085643, 0.269776237000278, + 0.6382189705979336, 0.9554129674543379, 0.6887405590404585, 0.5039371147315215, 0.8252078215260031, + 0.10805307033951939, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0.41724967962818993, 0.2781633024698039, 0, 0}, + {0.4002455779217782, 0.564838604931205, 0.1953327862932963, 0.7012116008559239, 0.8022033779885992, + 0.2808573734656151, 0.3000946668377774, 0.17230898600893108, 0, 0, 0, 0, 0, 0, 0, 0}, + {0.6461124772516227, 0.48674094781923605, 0.10036448207277027, 0.015471823700216714, 0.7689797229739221, + 0.03476124705509731, 0.8330787816937497, 0.35390071912245424, 0.18458690293633118, 0.42355207544691875, + 0.5798363871357116, 0.4243527922467508, 0.2879869168805991, 0.13736279699966614, 0.26166696186760385, + 0.9787373283247385, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0.1398260006661618, 0.12153200522616103, 0.7121966216410146, 0.9133849754561364, 0.7314612511926722, + 0.739267240037694, 0.7546027553155675, 0.342420614369761, 0, 0, 0, 0, 0, 0, 0, 0}, + {0.22574862111952854, 0.5690849507265368, 0.631317280107177, 0.8629733195605777, 0.3899681673812473, + 0.8794702418181954, 0.1517842815507917, 0.1657877194968821, 0.8083699897670112, 0.4355578268421948, + 0.1969236012819674, 0.06184299476225663, 0.07860704151155451, 0.397292331637211, 0.3839838104251525, + 0.26205058162224937, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0.33135924721579535, 0.5366302683143123, 0.25432093749208906, 0.7498876975281418, 0, 0, 0, 0}, + {0.6134308716246728, 0.09706486959071292, 0.356584692623391, 0.9766274230749799, 0.24172086288049444, + 0.7974589582847552, 0.7896787540010787, 0.5664999509048404, 0, 0, 0, 0, 0, 0, 0, 0}, + {0.18439523648303413, 0.5938145715696269, 0.5138768707589609, 0.03744323769215385, 0.9335962783171388, + 0.5763983101382048, 0.2806801388811724, 0.40858218250589795, 0, 0, 0, 0, 0, 0, 0, 0}, + {0.6665104076212911, 0.12712939621992436, 0, 0}, {0.07848088184489588, 0.9037683971776713, 0, 0}, + {0.2961816890284832, 0.6548352902037954, 0.004297273252101497, 0.6379597126860397, 0.394953301386181, + 0.3591579315172063, 0.5200829702509112, 0.7348916802637337, 0, 0, 0, 0, 0, 0, 0, 0}, + {0.15333899851758237, 0.9557388547562318, 0, 0}, + {0.24490652495244614, 0.764767557933974, 0.7412612945684217, 0.8345076232268165, 0.9713064054627377, + 0.1666699320547278, 0.6385013568574381, 0.02837645523199317, 0, 0, 0, 0, 0, 0, 0, 0}, + {0.43636393937714724, 0.5006457049751213, 0.29183886078443877, 0.7771528617739111, 0.9760896495636339, + 0.4211692751284558, 0.46136604887195976, 0.4385215837142291, 0.6480247707507675, 0.2869034885339521, + 0.21139535714638436, 0.623852932281303, 0.10723939013625783, 0.7423978700202447, 0.8339592164587588, + 0.6541139999860581, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0.673783082139485, 0.08489915452638419, 0.4428704894374401, 0.6322989775504414, 0, 0, 0, 0}, + {0.435204198844909, 0.60467095443427, 0.43492988438024205, 0.45115625691681205, 0.6073353909030185, + 0.7354160659126683, 0.6584642868836427, 0.49841087464599554, 0.3218054140407358, 0.8547287810979488, + 0.25920067933895863, 0.3577532915633288, 0.17696516490870162, 0.9810705645133889, 0.08037125000861489, + 0.9406431999485564, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0.14276827960992966, 0.13654559645650455, 0, 0}, + {0.10449615216668384, 0.8809636163201752, 0.23757224642400443, 0.6033844272609862, 0.3180497932853712, + 0.8000641846428788, 0.36691139380180937, 0.9936551508162524, 0, 0, 0, 0, 0, 0, 0, 0}, + {0.5619461607033829, 0.05587776081117257, 0.8242650361208828, 0.31679799606054226, 0.67922806899631, + 0.31896202698295617, 0.47844249679108297, 0.43194377662374206, 0, 0, 0, 0, 0, 0, 0, 0}, + {0.5928888719853808, 0.8700201519901183, 0.909292241027492, 0.1017088272754374, 0.7250571447087572, + 0.3172141314203841, 0.05139597560789155, 0.8469852045420578, 0.744706896124228, 0.24878503090173887, + 0.3248378441207508, 0.6159251794660624, 0.46869738424898, 0.42094147190658915, 0.2078392223975123, + 0.17713383848172415, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0.8259324464059233, 0.9375338155913502, 0.5557276593014934, 0.017649722866079798, 0, 0, 0, 0}}; - double[][][] outputData = { - { { 0.5613143313659362, -0.020146500453061336, 0.07086545895481336, -0.05003801442765281, - -0.06351635451036074, -0.03346768844048936, 0.07023899089706032, 0.007330763123826495, - 0.016022890367311193, 0.007330763123826495, 0.07023899089706032, -0.03346768844048936, - -0.06351635451036074, -0.05003801442765281, 0.07086545895481336, -0.020146500453061336 }, - { 0.0, -0.07513090216687965, 0.023854392878864396, -0.018752997939582024, -0.035626994964481226, - -0.07808216015046443, 0.036579082654843526, -0.10605270957897009, 0.0, - 0.10605270957897009, -0.036579082654843526, 0.07808216015046443, 0.035626994964481226, - 0.018752997939582024, -0.023854392878864396, 0.07513090216687965 } }, - { { 0.5247726717078615, 0.16295052246714098, -0.04560058213722554, -0.007228400216738096, - -0.08929769514117739, -0.007228400216738096, -0.04560058213722554, 0.16295052246714098 }, - { 0.0, 0.04148190873050992, -0.09855147775337295, 0.04068590273394563, 0.0, - -0.04068590273394563, 0.09855147775337295, -0.04148190873050992 } }, - { { 0.70499152784401, -0.16991889201466837, -0.20729882500410457, -0.16991889201466837 }, - { 0.0, -0.003303929642855835, 0.0, 0.003303929642855835 } }, - { { 0.31511419714426747, -0.09487924242021828, 0.11085303916164066, -0.09487924242021828 }, - { 0.0, -0.08655345247423127, 0.0, 0.08655345247423127 } }, - { { 0.527459017500086, 0.0053852573114735736, -0.16311343231991685, 0.0053852573114735736 }, - { 0.0, -0.1326118886568088, 0.0, 0.1326118886568088 } }, - { { 0.6102066942477578, -0.07202687894147122, -0.07873717780750517, -0.07202687894147122 }, - { 0.0, 0.15390946832427843, 0.0, -0.15390946832427843 } }, - { { 0.5522827825662304, 0.02247888349994264, 0.06894003068860452, 0.054250799183907905, - -0.04649970907457038, -0.025283389794838417, -0.02085901328343852, 0.07420721118594584, - -0.011511651888006347, 0.07420721118594584, -0.02085901328343852, -0.025283389794838417, - -0.04649970907457038, 0.054250799183907905, 0.06894003068860452, 0.02247888349994264 }, - { 0.0, 0.025696394507123577, -0.06942446748581371, 0.0016937241252907506, -0.10089121574342319, - 0.029974458022771634, 0.08492094910243575, -0.04639575646850304, 0.0, - 0.04639575646850304, -0.08492094910243575, -0.029974458022771634, 0.10089121574342319, - -0.0016937241252907506, 0.06942446748581371, -0.025696394507123577 } }, - { { 0.43415755465103323, -0.034896028361847214, -0.16002273910462933, -0.018817250009288352, - 0.1549843023935208, -0.018817250009288352, -0.16002273910462933, -0.034896028361847214 }, - { 0.0, 0.010730391426110649, 0.0701624828082627, 0.05047212452488589, 0.0, -0.05047212452488589, - -0.0701624828082627, -0.010730391426110649 } }, - { { 0.5562443791201288, 0.26587132099065325 }, { 0.0, 0.0 } }, - { { 0.2888899359571893, -0.1524121635077224 }, { 0.0, 0.0 } }, - { { 0.34577969092790256, 0.08022313425852307 }, { 0.0, 0.0 } }, - { { 0.4526769212546825, 0.19993603476671215, -0.03627467206261856, 0.19993603476671215 }, - { 0.0, 0.062078206122130886, 0.0, -0.062078206122130886 } }, - { { 0.6608149891546227, -0.3367713117114448 }, { 0.0, 0.0 } }, - { { 0.6300937093064057, 0.15833176475717037, -0.01800611030695498, 0.15833176475717037 }, - { 0.0, 0.1702159524768959, 0.0, -0.1702159524768959 } }, - { { 0.5904431377443209, 0.07954608309180142, -0.014921649796201489, 0.0005901097605300332, - -0.08174006039371018, 0.0005901097605300332, -0.014921649796201489, 0.07954608309180142 }, - { 0.0, 0.057018991161973565, -0.07891646065786176, 0.11081509003436779, 0.0, - -0.11081509003436779, 0.07891646065786176, -0.057018991161973565 } }, - { { 0.619036005447406, 0.36113904473552993 }, { 0.0, 0.0 } }, - { { 0.40575593293484286, -0.17603455720575084 }, { 0.0, 0.0 } }, - { { 0.6326754919239695, 0.1591418422174358, -0.22702101194404703, 0.1591418422174358 }, - { 0.0, -0.04548011120298867, 0.0, 0.04548011120298867 } }, - { { 0.4784014512253625, 0.011293452743340458, -0.056787040374204935, -0.023693104723780656, - 0.10951072764725725, 0.07974560959080311, -0.07394839942767145, 0.02677419921741667, - -0.010045968526969012, 0.02677419921741667, -0.07394839942767145, 0.07974560959080311, - 0.10951072764725725, -0.023693104723780656, -0.056787040374204935, 0.011293452743340458 }, - { 0.0, 0.07201888847998587, 0.03497215168058931, 0.05943123839853866, 0.05285004163673773, - -0.01085420783312048, 0.02035937888991158, 0.008553256069694384, 0.0, - -0.008553256069694384, -0.02035937888991158, 0.01085420783312048, -0.05285004163673773, - -0.05943123839853866, -0.03497215168058931, -0.07201888847998587 } }, - { { 0.4463826700338968, -0.06249810243977311 }, { 0.0, 0.0 } }, - { { 0.6138748297032897, 0.08206729827609008, -0.04196993291849642, 0.06274035723249816, - -0.06976035181410603, -0.05171556292590567, 0.00959458517750721, 0.06689841475792666, - -0.03127350909406679, 0.06689841475792666, 0.00959458517750721, -0.05171556292590567, - -0.06976035181410603, 0.06274035723249816, -0.04196993291849642, 0.08206729827609008 }, - { 0.0, 0.08138486461332381, 0.04127830896039093, -0.10717873814123505, -0.008051188471232124, - -0.02059807337669975, 0.056012288708000416, -0.0144978819539354, 0.0, - 0.0144978819539354, -0.056012288708000416, 0.02059807337669975, 0.008051188471232124, - 0.10717873814123505, -0.04127830896039093, -0.08138486461332381 } }, - { { 0.5212439749642962, 0.10242981444618521, 0.029577559990000446, 0.10242981444618521 }, - { 0.0, 0.1488184794663292, 0.0, -0.1488184794663292 } }, - { { 0.6297051883876085, -0.08399204761426496, -0.32076261758394786, -0.08399204761426496 }, - { 0.0, -0.007528757590112262, 0.0, 0.007528757590112262 } }, - { { 0.48407674552921653, -0.008227964517310823, 0.0213027140552919, 0.023595673719326214, - 0.005310690802590445, -0.06652446503281143, 0.08160932212853239, -0.06891582485441083, - 0.052892945583508655, -0.06891582485441083, 0.08160932212853239, -0.06652446503281143, - 0.005310690802590445, 0.023595673719326214, 0.0213027140552919, -0.008227964517310823 }, - { 0.0, -0.007093439306719466, -0.05389072430458301, -0.014648934153181905, - -0.029539361540359133, 0.028236671825160584, 0.07234447177670174, -0.03961094803635662, - 0.0, 0.03961094803635662, -0.07234447177670174, -0.028236671825160584, - 0.029539361540359133, 0.014648934153181905, 0.05389072430458301, - 0.007093439306719466 } }, - { { 0.40840858719165507, -0.00019587417138387797, 0.01785707194350384, -0.03945144723928065, - -0.05169950516677427, 0.06413973151079717, -0.1021740546918613, -0.03057286834254297, - 0.009965374816382294, -0.03057286834254297, -0.1021740546918613, 0.06413973151079717, - -0.05169950516677427, -0.03945144723928065, 0.01785707194350384, -0.00019587417138387797 }, - { 0.0, -0.05557309041453939, -0.13647515970329846, 0.09642410678380406, -0.027207097480659137, - -0.007933514822127903, 0.030346016285416798, 0.04319845964314343, 0.0, - -0.04319845964314343, -0.030346016285416798, 0.007933514822127903, 0.027207097480659137, - -0.09642410678380406, 0.13647515970329846, 0.05557309041453939 } }, - { { 0.6137287510109087, 0.019157896750848162, -0.05426698771160002, -0.107174755578878, - 0.04224619970375573, 0.039969043468570706, 0.004338296440140308, 0.06694608222595438, - 0.018542615219732883, 0.06694608222595438, 0.004338296440140308, 0.039969043468570706, - 0.04224619970375573, -0.107174755578878, -0.05426698771160002, 0.019157896750848162 }, - { 0.0, -0.06481804503251337, 0.01040922623102531, 0.03115577981125027, -0.03966726556078537, - 0.07314271886708576, 0.05386721014638309, -0.021237196104759097, 0.0, - 0.021237196104759097, -0.05386721014638309, -0.07314271886708576, 0.03966726556078537, - -0.03115577981125027, -0.01040922623102531, 0.06481804503251337 } }, - { { 0.5353487676283651, -0.0948504913692811, 0.04684477769694442, -0.013771271402122677, - -0.0203628406916192, 0.02514842443382726, 0.08972440209952712, 0.0023200127650800234, - 0.039423196857370835, 0.0023200127650800234, 0.08972440209952712, 0.02514842443382726, - -0.0203628406916192, -0.013771271402122677, 0.04684477769694442, -0.0948504913692811 }, - { 0.0, 0.02928471698851646, 0.005673137221436961, 0.0113450994077995, 0.07386064700676326, - -0.0921306795918339, -0.07150532757161823, -0.08177764531839596, 0.0, - 0.08177764531839596, 0.07150532757161823, 0.0921306795918339, -0.07386064700676326, - -0.0113450994077995, -0.005673137221436961, -0.02928471698851646 } }, - { { 0.6135828926485822, 0.12164819021773775, -0.037061355755697134, 0.09554922064593595, - 0.0014673678992679906, 0.09554922064593595, -0.037061355755697134, 0.12164819021773775 }, - { 0.0, -0.02511081481753711, -0.00724662177834573, 0.06830911674790396, 0.0, - -0.06830911674790396, 0.00724662177834573, 0.02511081481753711 } }, - { { 0.4992818447040142, -0.026335242639876785, -0.014162971662674932, -0.009204839208816325, - -0.0037880075406994396, -0.039667809092817426, 0.10573273706798308, 0.009221844764489362, - 0.10758448782129032, 0.009221844764489362, 0.10573273706798308, -0.039667809092817426, - -0.0037880075406994396, -0.009204839208816325, -0.014162971662674932, -0.026335242639876785 }, - { 0.0, -0.09052573027874725, 0.019800562825848633, 0.037129649810409254, -0.02593987015036732, - 0.010443783508403973, 0.04409059421606548, 0.019083289622832046, 0.0, - -0.019083289622832046, -0.04409059421606548, -0.010443783508403973, 0.02593987015036732, - -0.037129649810409254, -0.019800562825848633, 0.09052573027874725 } }, - { { 0.3477064910489969, 0.069543188579193 }, { 0.0, 0.0 } }, - { { 0.4271366217878908, -0.07189292138917516, 0.08837768784741297, -0.028596528627530097, - -0.002667519527527973, -0.028596528627530097, 0.08837768784741297, -0.07189292138917516 }, - { 0.0, 0.05875422493751868, -0.0034780760585043646, 0.08494469507363896, 0.0, - -0.08494469507363896, 0.0034780760585043646, -0.05875422493751868 } }, - { { 0.4073432727204618, 0.014805197956703195, 0.05793821643397497, 0.10942961383389802, - 0.0070449629545399906, 0.04114695896896992, -0.08622162389229589, -0.05000037718074825, - 0.05048330638107701, -0.05000037718074825, -0.08622162389229589, 0.04114695896896992, - 0.0070449629545399906, 0.10942961383389802, 0.05793821643397497, 0.014805197956703195 }, - { 0.0, -0.008842335656083382, -0.032743438452449085, -0.046196276979382764, - -0.04312784975457762, 0.005801406633435733, 0.019074670841659862, -0.07709285356659565, - 0.0, 0.07709285356659565, -0.019074670841659862, -0.005801406633435733, - 0.04312784975457762, 0.046196276979382764, 0.032743438452449085, - 0.008842335656083382 } }, - { { 0.5568364329881461, -0.17902159944745377, -0.07443901563721851, 0.031112786815826174, - 0.02768522421570796, 0.031112786815826174, -0.07443901563721851, -0.17902159944745377 }, - { 0.0, -0.009434766955953945, -0.04937579307025532, 0.0011667664626842864, 0.0, - -0.0011667664626842864, 0.04937579307025532, 0.009434766955953945 } }, - { { 0.4062976724756584, 0.013942954902943575, 0.0013568203618884583, -0.07879442082783945, - 0.008667802900890809, -0.05295232243525326, 0.0693361048873288, -0.027851553801721537, - -0.047959823332604595, -0.027851553801721537, 0.0693361048873288, -0.05295232243525326, - 0.008667802900890809, -0.07879442082783945, 0.0013568203618884583, 0.013942954902943575 }, - { 0.0, 0.10338853801320178, 0.02821697248022432, -0.03906633642088774, 0.05804692097388578, - -0.018017802505488005, -0.008342126196425707, 0.04659679046117832, 0.0, - -0.04659679046117832, 0.008342126196425707, 0.018017802505488005, -0.05804692097388578, - 0.03906633642088774, -0.02821697248022432, -0.10338853801320178 } }, - { { 0.4680495376375846, 0.019259577430926572, -0.17520944528364238, 0.019259577430926572 }, - { 0.0, -0.05331435730345738, 0.0, 0.05331435730345738 } }, - { { 0.5548832978731156, -0.05169341469959422, -0.0363889640149128, 0.14462091688563883, - -0.054529502590706436, 0.14462091688563883, -0.0363889640149128, -0.05169341469959422 }, - { 0.0, -0.07979294428422681, -0.081075443263044, 0.028480571060195102, 0.0, - -0.028480571060195102, 0.081075443263044, 0.07979294428422681 } }, - { { 0.44109835329327374, -0.059306377579101247, 0.04042931314500495, -0.12799388287942493, - 0.03703877781680284, -0.12799388287942493, 0.04042931314500495, -0.059306377579101247 }, - { 0.0, -0.0021153720251691766, 0.09052343268872248, -0.060414554994616315, 0.0, - 0.060414554994616315, -0.09052343268872248, 0.0021153720251691766 } }, - { { 0.39681990192060773, 0.26969050570068337 }, { 0.0, 0.0 } }, - { { 0.4911246395112836, -0.4126437576663877 }, { 0.0, 0.0 } }, - { { 0.4502949810735565, 0.022355638075107313, 0.020844343363956436, -0.04704854116453177, - -0.14641617259413728, -0.04704854116453177, 0.020844343363956436, 0.022355638075107313 }, - { 0.0, -0.04690643540201719, -0.044857271403596466, 0.08203998884768524, 0.0, - -0.08203998884768524, 0.044857271403596466, 0.04690643540201719 } }, - { { 0.5545389266369071, -0.4011999281193247 }, { 0.0, 0.0 } }, - { { 0.5487871437860694, -0.10918772610680917, -0.0204437151263345, -0.07241224402076372, - 0.10020675167419157, -0.07241224402076372, -0.0204437151263345, -0.10918772610680917 }, - { 0.0, 0.1369624550245931, 0.008569176441236517, 0.11127247059684721, 0.0, -0.11127247059684721, - -0.008569176441236517, -0.1369624550245931 } }, - { { 0.525689684343914, 0.04293325970025039, -0.030179739992632723, -0.05038212963216292, - 0.02307239166039156, -0.0161186370831989, 0.0303121987961356, -0.029347700828293622, - -0.02990503020774543, -0.029347700828293622, 0.0303121987961356, -0.0161186370831989, - 0.02307239166039156, -0.05038212963216292, -0.030179739992632723, 0.04293325970025039 }, - { 0.0, 0.031650959576365614, -0.05249532649964127, -0.06330481947587874, -0.03390781494360795, - 0.07112408901498432, 0.04651605442534566, -0.05113269678961534, 0.0, - 0.05113269678961534, -0.04651605442534566, -0.07112408901498432, 0.03390781494360795, - 0.06330481947587874, 0.05249532649964127, -0.031650959576365614 } }, - { { 0.45846292591343774, 0.057728148175511224, 0.09986385987502491, 0.057728148175511224 }, - { 0.0, -0.1368499557560143, 0.0, 0.1368499557560143 } }, - { { 0.524882891146362, 0.008511396928025477, 0.014781289867711371, 0.009887920266107053, - 0.006771504255369168, 0.039851202083645135, -0.018192657733470778, -0.029900823076734365, - -0.15309835748275913, -0.029900823076734365, -0.018192657733470778, 0.039851202083645135, - 0.006771504255369168, 0.009887920266107053, 0.014781289867711371, 0.008511396928025477 }, - { 0.0, 0.03486331238468826, -0.04200452046561043, -0.029916623097555238, 0.05799517143022398, - -0.04274944723179936, -0.03641639881897832, -0.08556206824813507, 0.0, - 0.08556206824813507, 0.03641639881897832, 0.04274944723179936, -0.05799517143022398, - 0.029916623097555238, 0.04200452046561043, -0.03486331238468826 } }, - { { 0.1396569380332171, 0.0031113415767125563 }, { 0.0, 0.0 } }, - { { 0.5381371205897701, 0.014951746342371994, -0.022742211846719845, -0.06834015662204383, - -0.281379724170303, -0.06834015662204383, -0.022742211846719845, 0.014951746342371994 }, - { 0.0, -0.043512210721145014, 0.010498527860726914, -0.011177423876693775, 0.0, - 0.011177423876693775, -0.010498527860726914, 0.043512210721145014 } }, - { { 0.458432915386259, -0.027736276833153158, -0.007691662901534085, -0.001584200240078635, - 0.17753752526665573, -0.001584200240078635, -0.007691662901534085, -0.027736276833153158 }, - { 0.0, 0.009796688554373531, -0.04673774811126945, -0.07665894627807641, 0.0, - 0.07665894627807641, 0.04673774811126945, -0.009796688554373531 } }, - { { 0.476464338512819, 0.010629551641227537, 0.03935742410033851, -0.01969212546385932, - 0.06487406336960619, -0.0647718460563506, -0.02137726920635457, 0.03587991384427056, - 0.02662510901480497, 0.03587991384427056, -0.02137726920635457, -0.0647718460563506, - 0.06487406336960619, -0.01969212546385932, 0.03935742410033851, 0.010629551641227537 }, - { 0.0, 0.030136326600295898, 0.06420857116384811, 0.09222343873200076, 0.0072004835283467905, - 0.08643721045556424, -0.05765328972900675, -0.039739841791084904, 0.0, - 0.039739841791084904, 0.05765328972900675, -0.08643721045556424, -0.0072004835283467905, - -0.09222343873200076, -0.06420857116384811, -0.030136326600295898 } }, - { { 0.5842109110412117, 0.06755119677610749, 0.10661914181249668, 0.06755119677610749 }, - { 0.0, 0.2299710231813176, 0.0, -0.2299710231813176 } } - }; + double[][][] outputData = { + {{0.5613143313659362, -0.020146500453061336, 0.07086545895481336, -0.05003801442765281, + -0.06351635451036074, -0.03346768844048936, 0.07023899089706032, 0.007330763123826495, + 0.016022890367311193, 0.007330763123826495, 0.07023899089706032, -0.03346768844048936, + -0.06351635451036074, -0.05003801442765281, 0.07086545895481336, -0.020146500453061336}, + {0.0, -0.07513090216687965, 0.023854392878864396, -0.018752997939582024, -0.035626994964481226, + -0.07808216015046443, 0.036579082654843526, -0.10605270957897009, 0.0, 0.10605270957897009, + -0.036579082654843526, 0.07808216015046443, 0.035626994964481226, 0.018752997939582024, + -0.023854392878864396, 0.07513090216687965}}, + {{0.5247726717078615, 0.16295052246714098, -0.04560058213722554, -0.007228400216738096, + -0.08929769514117739, -0.007228400216738096, -0.04560058213722554, 0.16295052246714098}, + {0.0, 0.04148190873050992, -0.09855147775337295, 0.04068590273394563, 0.0, -0.04068590273394563, + 0.09855147775337295, -0.04148190873050992}}, + {{0.70499152784401, -0.16991889201466837, -0.20729882500410457, -0.16991889201466837}, + {0.0, -0.003303929642855835, 0.0, 0.003303929642855835}}, + {{0.31511419714426747, -0.09487924242021828, 0.11085303916164066, -0.09487924242021828}, + {0.0, -0.08655345247423127, 0.0, 0.08655345247423127}}, + {{0.527459017500086, 0.0053852573114735736, -0.16311343231991685, 0.0053852573114735736}, + {0.0, -0.1326118886568088, 0.0, 0.1326118886568088}}, + {{0.6102066942477578, -0.07202687894147122, -0.07873717780750517, -0.07202687894147122}, + {0.0, 0.15390946832427843, 0.0, -0.15390946832427843}}, + {{0.5522827825662304, 0.02247888349994264, 0.06894003068860452, 0.054250799183907905, -0.04649970907457038, + -0.025283389794838417, -0.02085901328343852, 0.07420721118594584, -0.011511651888006347, + 0.07420721118594584, -0.02085901328343852, -0.025283389794838417, -0.04649970907457038, + 0.054250799183907905, 0.06894003068860452, 0.02247888349994264}, + {0.0, 0.025696394507123577, -0.06942446748581371, 0.0016937241252907506, -0.10089121574342319, + 0.029974458022771634, 0.08492094910243575, -0.04639575646850304, 0.0, 0.04639575646850304, + -0.08492094910243575, -0.029974458022771634, 0.10089121574342319, -0.0016937241252907506, + 0.06942446748581371, -0.025696394507123577}}, + {{0.43415755465103323, -0.034896028361847214, -0.16002273910462933, -0.018817250009288352, + 0.1549843023935208, -0.018817250009288352, -0.16002273910462933, -0.034896028361847214}, + {0.0, 0.010730391426110649, 0.0701624828082627, 0.05047212452488589, 0.0, -0.05047212452488589, + -0.0701624828082627, -0.010730391426110649}}, + {{0.5562443791201288, 0.26587132099065325}, {0.0, 0.0}}, + {{0.2888899359571893, -0.1524121635077224}, {0.0, 0.0}}, + {{0.34577969092790256, 0.08022313425852307}, {0.0, 0.0}}, + {{0.4526769212546825, 0.19993603476671215, -0.03627467206261856, 0.19993603476671215}, + {0.0, 0.062078206122130886, 0.0, -0.062078206122130886}}, + {{0.6608149891546227, -0.3367713117114448}, {0.0, 0.0}}, + {{0.6300937093064057, 0.15833176475717037, -0.01800611030695498, 0.15833176475717037}, + {0.0, 0.1702159524768959, 0.0, -0.1702159524768959}}, + {{0.5904431377443209, 0.07954608309180142, -0.014921649796201489, 0.0005901097605300332, + -0.08174006039371018, 0.0005901097605300332, -0.014921649796201489, 0.07954608309180142}, + {0.0, 0.057018991161973565, -0.07891646065786176, 0.11081509003436779, 0.0, -0.11081509003436779, + 0.07891646065786176, -0.057018991161973565}}, + {{0.619036005447406, 0.36113904473552993}, {0.0, 0.0}}, + {{0.40575593293484286, -0.17603455720575084}, {0.0, 0.0}}, + {{0.6326754919239695, 0.1591418422174358, -0.22702101194404703, 0.1591418422174358}, + {0.0, -0.04548011120298867, 0.0, 0.04548011120298867}}, + {{0.4784014512253625, 0.011293452743340458, -0.056787040374204935, -0.023693104723780656, + 0.10951072764725725, 0.07974560959080311, -0.07394839942767145, 0.02677419921741667, + -0.010045968526969012, 0.02677419921741667, -0.07394839942767145, 0.07974560959080311, + 0.10951072764725725, -0.023693104723780656, -0.056787040374204935, 0.011293452743340458}, + {0.0, 0.07201888847998587, 0.03497215168058931, 0.05943123839853866, 0.05285004163673773, + -0.01085420783312048, 0.02035937888991158, 0.008553256069694384, 0.0, -0.008553256069694384, + -0.02035937888991158, 0.01085420783312048, -0.05285004163673773, -0.05943123839853866, + -0.03497215168058931, -0.07201888847998587}}, + {{0.4463826700338968, -0.06249810243977311}, {0.0, 0.0}}, + {{0.6138748297032897, 0.08206729827609008, -0.04196993291849642, 0.06274035723249816, -0.06976035181410603, + -0.05171556292590567, 0.00959458517750721, 0.06689841475792666, -0.03127350909406679, + 0.06689841475792666, 0.00959458517750721, -0.05171556292590567, -0.06976035181410603, + 0.06274035723249816, -0.04196993291849642, 0.08206729827609008}, + {0.0, 0.08138486461332381, 0.04127830896039093, -0.10717873814123505, -0.008051188471232124, + -0.02059807337669975, 0.056012288708000416, -0.0144978819539354, 0.0, 0.0144978819539354, + -0.056012288708000416, 0.02059807337669975, 0.008051188471232124, 0.10717873814123505, + -0.04127830896039093, -0.08138486461332381}}, + {{0.5212439749642962, 0.10242981444618521, 0.029577559990000446, 0.10242981444618521}, + {0.0, 0.1488184794663292, 0.0, -0.1488184794663292}}, + {{0.6297051883876085, -0.08399204761426496, -0.32076261758394786, -0.08399204761426496}, + {0.0, -0.007528757590112262, 0.0, 0.007528757590112262}}, + {{0.48407674552921653, -0.008227964517310823, 0.0213027140552919, 0.023595673719326214, + 0.005310690802590445, -0.06652446503281143, 0.08160932212853239, -0.06891582485441083, + 0.052892945583508655, -0.06891582485441083, 0.08160932212853239, -0.06652446503281143, + 0.005310690802590445, 0.023595673719326214, 0.0213027140552919, -0.008227964517310823}, + {0.0, -0.007093439306719466, -0.05389072430458301, -0.014648934153181905, -0.029539361540359133, + 0.028236671825160584, 0.07234447177670174, -0.03961094803635662, 0.0, 0.03961094803635662, + -0.07234447177670174, -0.028236671825160584, 0.029539361540359133, 0.014648934153181905, + 0.05389072430458301, 0.007093439306719466}}, + {{0.40840858719165507, -0.00019587417138387797, 0.01785707194350384, -0.03945144723928065, + -0.05169950516677427, 0.06413973151079717, -0.1021740546918613, -0.03057286834254297, + 0.009965374816382294, -0.03057286834254297, -0.1021740546918613, 0.06413973151079717, + -0.05169950516677427, -0.03945144723928065, 0.01785707194350384, -0.00019587417138387797}, + {0.0, -0.05557309041453939, -0.13647515970329846, 0.09642410678380406, -0.027207097480659137, + -0.007933514822127903, 0.030346016285416798, 0.04319845964314343, 0.0, -0.04319845964314343, + -0.030346016285416798, 0.007933514822127903, 0.027207097480659137, -0.09642410678380406, + 0.13647515970329846, 0.05557309041453939}}, + {{0.6137287510109087, 0.019157896750848162, -0.05426698771160002, -0.107174755578878, 0.04224619970375573, + 0.039969043468570706, 0.004338296440140308, 0.06694608222595438, 0.018542615219732883, + 0.06694608222595438, 0.004338296440140308, 0.039969043468570706, 0.04224619970375573, + -0.107174755578878, -0.05426698771160002, 0.019157896750848162}, + {0.0, -0.06481804503251337, 0.01040922623102531, 0.03115577981125027, -0.03966726556078537, + 0.07314271886708576, 0.05386721014638309, -0.021237196104759097, 0.0, 0.021237196104759097, + -0.05386721014638309, -0.07314271886708576, 0.03966726556078537, -0.03115577981125027, + -0.01040922623102531, 0.06481804503251337}}, + {{0.5353487676283651, -0.0948504913692811, 0.04684477769694442, -0.013771271402122677, -0.0203628406916192, + 0.02514842443382726, 0.08972440209952712, 0.0023200127650800234, 0.039423196857370835, + 0.0023200127650800234, 0.08972440209952712, 0.02514842443382726, -0.0203628406916192, + -0.013771271402122677, 0.04684477769694442, -0.0948504913692811}, + {0.0, 0.02928471698851646, 0.005673137221436961, 0.0113450994077995, 0.07386064700676326, + -0.0921306795918339, -0.07150532757161823, -0.08177764531839596, 0.0, 0.08177764531839596, + 0.07150532757161823, 0.0921306795918339, -0.07386064700676326, -0.0113450994077995, + -0.005673137221436961, -0.02928471698851646}}, + {{0.6135828926485822, 0.12164819021773775, -0.037061355755697134, 0.09554922064593595, + 0.0014673678992679906, 0.09554922064593595, -0.037061355755697134, 0.12164819021773775}, + {0.0, -0.02511081481753711, -0.00724662177834573, 0.06830911674790396, 0.0, -0.06830911674790396, + 0.00724662177834573, 0.02511081481753711}}, + {{0.4992818447040142, -0.026335242639876785, -0.014162971662674932, -0.009204839208816325, + -0.0037880075406994396, -0.039667809092817426, 0.10573273706798308, 0.009221844764489362, + 0.10758448782129032, 0.009221844764489362, 0.10573273706798308, -0.039667809092817426, + -0.0037880075406994396, -0.009204839208816325, -0.014162971662674932, -0.026335242639876785}, + {0.0, -0.09052573027874725, 0.019800562825848633, 0.037129649810409254, -0.02593987015036732, + 0.010443783508403973, 0.04409059421606548, 0.019083289622832046, 0.0, -0.019083289622832046, + -0.04409059421606548, -0.010443783508403973, 0.02593987015036732, -0.037129649810409254, + -0.019800562825848633, 0.09052573027874725}}, + {{0.3477064910489969, 0.069543188579193}, {0.0, 0.0}}, + {{0.4271366217878908, -0.07189292138917516, 0.08837768784741297, -0.028596528627530097, + -0.002667519527527973, -0.028596528627530097, 0.08837768784741297, -0.07189292138917516}, + {0.0, 0.05875422493751868, -0.0034780760585043646, 0.08494469507363896, 0.0, -0.08494469507363896, + 0.0034780760585043646, -0.05875422493751868}}, + {{0.4073432727204618, 0.014805197956703195, 0.05793821643397497, 0.10942961383389802, 0.0070449629545399906, + 0.04114695896896992, -0.08622162389229589, -0.05000037718074825, 0.05048330638107701, + -0.05000037718074825, -0.08622162389229589, 0.04114695896896992, 0.0070449629545399906, + 0.10942961383389802, 0.05793821643397497, 0.014805197956703195}, + {0.0, -0.008842335656083382, -0.032743438452449085, -0.046196276979382764, -0.04312784975457762, + 0.005801406633435733, 0.019074670841659862, -0.07709285356659565, 0.0, 0.07709285356659565, + -0.019074670841659862, -0.005801406633435733, 0.04312784975457762, 0.046196276979382764, + 0.032743438452449085, 0.008842335656083382}}, + {{0.5568364329881461, -0.17902159944745377, -0.07443901563721851, 0.031112786815826174, 0.02768522421570796, + 0.031112786815826174, -0.07443901563721851, -0.17902159944745377}, + {0.0, -0.009434766955953945, -0.04937579307025532, 0.0011667664626842864, 0.0, -0.0011667664626842864, + 0.04937579307025532, 0.009434766955953945}}, + {{0.4062976724756584, 0.013942954902943575, 0.0013568203618884583, -0.07879442082783945, + 0.008667802900890809, -0.05295232243525326, 0.0693361048873288, -0.027851553801721537, + -0.047959823332604595, -0.027851553801721537, 0.0693361048873288, -0.05295232243525326, + 0.008667802900890809, -0.07879442082783945, 0.0013568203618884583, 0.013942954902943575}, + {0.0, 0.10338853801320178, 0.02821697248022432, -0.03906633642088774, 0.05804692097388578, + -0.018017802505488005, -0.008342126196425707, 0.04659679046117832, 0.0, -0.04659679046117832, + 0.008342126196425707, 0.018017802505488005, -0.05804692097388578, 0.03906633642088774, + -0.02821697248022432, -0.10338853801320178}}, + {{0.4680495376375846, 0.019259577430926572, -0.17520944528364238, 0.019259577430926572}, + {0.0, -0.05331435730345738, 0.0, 0.05331435730345738}}, + {{0.5548832978731156, -0.05169341469959422, -0.0363889640149128, 0.14462091688563883, -0.054529502590706436, + 0.14462091688563883, -0.0363889640149128, -0.05169341469959422}, + {0.0, -0.07979294428422681, -0.081075443263044, 0.028480571060195102, 0.0, -0.028480571060195102, + 0.081075443263044, 0.07979294428422681}}, + {{0.44109835329327374, -0.059306377579101247, 0.04042931314500495, -0.12799388287942493, + 0.03703877781680284, -0.12799388287942493, 0.04042931314500495, -0.059306377579101247}, + {0.0, -0.0021153720251691766, 0.09052343268872248, -0.060414554994616315, 0.0, 0.060414554994616315, + -0.09052343268872248, 0.0021153720251691766}}, + {{0.39681990192060773, 0.26969050570068337}, {0.0, 0.0}}, + {{0.4911246395112836, -0.4126437576663877}, {0.0, 0.0}}, + {{0.4502949810735565, 0.022355638075107313, 0.020844343363956436, -0.04704854116453177, + -0.14641617259413728, -0.04704854116453177, 0.020844343363956436, 0.022355638075107313}, + {0.0, -0.04690643540201719, -0.044857271403596466, 0.08203998884768524, 0.0, -0.08203998884768524, + 0.044857271403596466, 0.04690643540201719}}, + {{0.5545389266369071, -0.4011999281193247}, {0.0, 0.0}}, + {{0.5487871437860694, -0.10918772610680917, -0.0204437151263345, -0.07241224402076372, 0.10020675167419157, + -0.07241224402076372, -0.0204437151263345, -0.10918772610680917}, + {0.0, 0.1369624550245931, 0.008569176441236517, 0.11127247059684721, 0.0, -0.11127247059684721, + -0.008569176441236517, -0.1369624550245931}}, + {{0.525689684343914, 0.04293325970025039, -0.030179739992632723, -0.05038212963216292, 0.02307239166039156, + -0.0161186370831989, 0.0303121987961356, -0.029347700828293622, -0.02990503020774543, + -0.029347700828293622, 0.0303121987961356, -0.0161186370831989, 0.02307239166039156, + -0.05038212963216292, -0.030179739992632723, 0.04293325970025039}, + {0.0, 0.031650959576365614, -0.05249532649964127, -0.06330481947587874, -0.03390781494360795, + 0.07112408901498432, 0.04651605442534566, -0.05113269678961534, 0.0, 0.05113269678961534, + -0.04651605442534566, -0.07112408901498432, 0.03390781494360795, 0.06330481947587874, + 0.05249532649964127, -0.031650959576365614}}, + {{0.45846292591343774, 0.057728148175511224, 0.09986385987502491, 0.057728148175511224}, + {0.0, -0.1368499557560143, 0.0, 0.1368499557560143}}, + {{0.524882891146362, 0.008511396928025477, 0.014781289867711371, 0.009887920266107053, 0.006771504255369168, + 0.039851202083645135, -0.018192657733470778, -0.029900823076734365, -0.15309835748275913, + -0.029900823076734365, -0.018192657733470778, 0.039851202083645135, 0.006771504255369168, + 0.009887920266107053, 0.014781289867711371, 0.008511396928025477}, + {0.0, 0.03486331238468826, -0.04200452046561043, -0.029916623097555238, 0.05799517143022398, + -0.04274944723179936, -0.03641639881897832, -0.08556206824813507, 0.0, 0.08556206824813507, + 0.03641639881897832, 0.04274944723179936, -0.05799517143022398, 0.029916623097555238, + 0.04200452046561043, -0.03486331238468826}}, + {{0.1396569380332171, 0.0031113415767125563}, {0.0, 0.0}}, + {{0.5381371205897701, 0.014951746342371994, -0.022742211846719845, -0.06834015662204383, -0.281379724170303, + -0.06834015662204383, -0.022742211846719845, 0.014951746342371994}, + {0.0, -0.043512210721145014, 0.010498527860726914, -0.011177423876693775, 0.0, 0.011177423876693775, + -0.010498527860726914, 0.043512210721145014}}, + {{0.458432915386259, -0.027736276833153158, -0.007691662901534085, -0.001584200240078635, + 0.17753752526665573, -0.001584200240078635, -0.007691662901534085, -0.027736276833153158}, + {0.0, 0.009796688554373531, -0.04673774811126945, -0.07665894627807641, 0.0, 0.07665894627807641, + 0.04673774811126945, -0.009796688554373531}}, + {{0.476464338512819, 0.010629551641227537, 0.03935742410033851, -0.01969212546385932, 0.06487406336960619, + -0.0647718460563506, -0.02137726920635457, 0.03587991384427056, 0.02662510901480497, + 0.03587991384427056, -0.02137726920635457, -0.0647718460563506, 0.06487406336960619, + -0.01969212546385932, 0.03935742410033851, 0.010629551641227537}, + {0.0, 0.030136326600295898, 0.06420857116384811, 0.09222343873200076, 0.0072004835283467905, + 0.08643721045556424, -0.05765328972900675, -0.039739841791084904, 0.0, 0.039739841791084904, + 0.05765328972900675, -0.08643721045556424, -0.0072004835283467905, -0.09222343873200076, + -0.06420857116384811, -0.030136326600295898}}, + {{0.5842109110412117, 0.06755119677610749, 0.10661914181249668, 0.06755119677610749}, + {0.0, 0.2299710231813176, 0.0, -0.2299710231813176}}}; - for (int i = 0; i < inputData.length; i++) { - double[] re = inputData[i]; // Real part of input - double[] im = new double[re.length]; // Imaginary part of input (all zeros for real IFFT) + for(int i = 0; i < inputData.length; i++) { + double[] re = inputData[i]; // Real part of input + double[] im = new double[re.length]; // Imaginary part of input (all zeros for real IFFT) - double[][] expected = outputData[i]; // Expected output + double[][] expected = outputData[i]; // Expected output - ifft(re, im, 1, re.length); // Perform IFFT + ifft(re, im, 1, re.length); // Perform IFFT - double[][] actual = new double[][] { re, im }; - // Validate the IFFT results - validateFftResults(expected, actual, i + 1); - } - } + double[][] actual = new double[][] {re, im}; + // Validate the IFFT results + validateFftResults(expected, actual, i + 1); + } + } - @Test - public void testIfftWithComplexNumpyData() { + @Test + public void testIfftWithComplexNumpyData() { - // Define the input data array (complex IFFT inputs) - double[][] inputData = { - { 0.17768499045697306, 0.3405035491673728, 0.9272906450602005, 0.28247504052271843, 0.42775517613102865, - 0.8338783039818357, 0.9813749624557385, 0.47224489612381737, 0.7936831995784907, - 0.5584182145651306, 0.5296113722056018, 0.6687593295928902, 0.9630598447622862, - 0.7130539473424196, 0.860081483892192, 0.8985058305053549 }, - { 0.5574117454125004, 0.5153534964811869, 0.2218520797445649, 0.6278331124271054, 0.5264697956167971, - 0.9311289722152635, 0.13031360760348354, 0.6303629910772452 }, - { 0.8630369856754253, 0.9497763465868887, 0.9997691100440745, 0.3303206858594411, 0.9418293674995452, - 0.836702533317602, 0.42360226191376127, 0.28470444389038796, 0.18653359615821063, - 0.6164549770372061, 0.22800778967348623, 0.8045503278555484, 0.7141418111594348, - 0.8898346433405011, 0.5648709181981051, 0.7063388262940075, 0.33464462295280606, - 0.5338979798606405, 0.3604790551977437, 0.4551044306632025, 0.023102489774489032, - 0.5939484248343428, 0.601633008560747, 0.5056641001742173, 0.4945185946051195, - 0.3632887663114238, 0.09711935047598264, 0.8610061039014398, 0.1695426000726007, - 0.5420844999569931, 0.22423519306858575, 0.8138053316534443 }, - { 0.2668982957216445, 0.6518643198476277, 0.38293047888504705, 0.5276109887708621, 0.800891602234696, - 0.665489651348857, 0.5102919050998651, 0.03294208149274114 }, - { 0.8673593019099284, 0.5449051089951431, 0.35927077618823344, 0.9657806185363754 }, - { 0.3275052635434117, 0.13324953722224242, 0.9813054584386846, 0.41940627067946346, 0.5854578390799309, - 0.9370081210453547, 0.4965403182131879, 0.40441779810528955, 0.5902823763509569, - 0.2816096727771966, 0.9187065056993756, 0.01750173716395509, 0.9430939103428495, - 0.524796393731078, 0.6522622245916397, 0.7191101906183052, 0.924218305299526, - 0.41557703109579636, 0.27205034406713446, 0.9913812499805844, 0.8474192033613039, - 0.7090188824891868, 0.6353901388073759, 0.9455732252453851, 0.5575374113914232, - 0.8165674671938592, 0.9677824877594866, 0.4330735440317255, 0.9448510262521692, - 0.4022170497451182, 0.6565890617824428, 0.3310258105072468 }, - { 0.7856782800988511, 0.6679548733836229, 0.143535331535097, 0.7943042164876586 }, - { 0.04873326306922765, 0.15565317347954144, 0.25192661757985957, 0.4991677708509388, 0.3066370428993622, - 0.16093416033095542, 0.7306599702801884, 0.3010950973671792, 0.8167182072302016, - 0.35620787574024504, 0.08043055169163749, 0.3186418802351839, 0.20232598085979203, - 0.9823046248429211, 0.1332517204586674, 0.6984885530480028, 0.2197570335512251, - 0.3823643227597068, 0.2993821619426701, 0.7890734732591685, 0.9150357080758154, - 0.814073309530792, 0.6466925662323889, 0.21367632233614853, 0.9317957911629086, - 0.902898345030541, 0.6272897679127241, 0.5146060996086259, 0.47521808342344796, - 0.11893945126711991, 0.8681215624426699, 0.8562757163749677 }, - { 0.2813501281398796, 0.423403543513415, 0.06307283742785219, 0.1410750085202912, 0.4171503325036342, - 0.4946182389939746, 0.6531869179899719, 0.10773798427753578 }, - { 0.3042057982656987, 0.36010750174274897, 0.9210922929503627, 0.9192485611716222, 0.5144480274153918, - 0.5814672279115486, 0.6011866421864879, 0.7012821521290435 }, - { 0.9995986452888619, 0.18328112493260273, 0.38935455191203494, 0.3509391406504685, 0.3263088507735412, - 0.9648828416159605, 0.8423623129564872, 0.3871067276945551, 0.45097017756014, - 0.5159198092355896, 0.954050553063797, 0.5014833734710475, 0.7976092097295954, - 0.43190395198337117, 0.12323995739627724, 0.04493903174415104 }, - { 0.07843773812596688, 0.7221954247862206, 0.5131226646525481, 0.9489126584469372, 0.47532447735279626, - 0.5050483053898411, 0.11285163895281858, 0.6442680036899163, 0.6943999719718427, - 0.11854357006570826, 0.9433634243731835, 0.8471613997721141, 0.9645297215939213, - 0.9946433175329309, 0.9445985285947038, 0.07499800505216225 }, - { 0.025606890585376463, 0.12419720934757339, 0.6863742021872193, 0.1591793804631091, - 0.20073179883607617, 0.9163422318494329, 0.8255291764015206, 0.24839917554927815, - 0.9017306407233789, 0.011143738439888695, 0.9499466038149739, 0.23839793578917512, - 0.7530603927443468, 0.7221433441575709, 0.9882231809584526, 0.010882536537176746, - 0.6372138133730175, 0.9850167826732869, 0.6096297429504447, 0.6421948734889896, - 0.4673898464886783, 0.8286049778392416, 0.7412286660738077, 0.7568564633736149, - 0.512257077801464, 0.7639553542893256, 0.019409959095512463, 0.44888036476478865, - 0.5561388696313153, 0.4010303093814831, 0.3519885049606841, 0.36180934217779015 }, - { 0.42957497934079614, 0.3494574872550452, 0.28432737366460115, 0.6512952245877861 }, - { 0.9004930907404661, 0.24177853554090678, 0.2965518122533567, 0.15880453298324915, 0.808380071501632, - 0.534445489902122, 0.6553342025721992, 0.3775806858178651 }, - { 0.05039928002703009, 0.6153252657122776, 0.18969212975794214, 0.6401253643448036, 0.6169843888554394, - 0.8312875187886041, 0.9490105226327502, 0.7245238257008109 }, - { 0.05875630673478127, 0.02409603626667045, 0.8073637232586641, 0.5978959407049632, 0.673078922215489, - 0.5196326131440167, 0.4738950710992147, 0.032500830859891416, 0.8810220711016585, - 0.12909173283303255, 0.9213328273665816, 0.8090874942954726, 0.9354909447748617, - 0.882576819693844, 0.04917162075490544, 0.49253892564640533, 0.3898160399036703, - 0.13157304806755854, 0.6997084267185423, 0.23507481777217032, 0.07693269358017651, - 0.41923240484513036, 0.7701296835014443, 0.9230307316770667, 0.44276343787221584, - 0.4556723674991543, 0.0005269550147345425, 0.9469237088461168, 0.43039381632701756, - 0.9818691560857561, 0.0032385749365969607, 0.38460732810872156 }, - { 0.07177251361521142, 0.597972082172478, 0.6226088866570731, 0.7447140422117096, 0.03608317332120958, - 0.9403392994904969, 0.4214767886988283, 0.5400819129156715 }, - { 0.7886913760270524, 0.2811741704130385, 0.5337400451604049, 0.8427268522792017, 0.43468289560698725, - 0.4474995478449879, 0.4384795264165595, 0.9811467374329375, 0.07392345242810194, - 0.1727386149139265, 0.9717684346647587, 0.9363308310478798, 0.5655367186251736, - 0.7638950609861564, 0.7938449219818952, 0.1853731675532243 }, - { 0.8599654952652722, 0.9067031377676662, 0.9192926722382764, 0.7337945168999715, 0.13609124919880633, - 0.9797115711637998, 0.3065888944026701, 0.1660206808739042, 0.27230041646280134, - 0.3066077606192541, 0.2953550472086268, 0.921215997827601, 0.9959872895290139, - 0.7446027654086131, 0.32904544352394216, 0.3278728444599871 }, - { 0.7347977442629331, 0.2823105544640442, 0.06972698993685245, 0.024892539689898574, 0.8377112725721335, - 0.795264231632406, 0.9831660948336206, 0.6726586022412739, 0.23734748257972293, - 0.27596016754533936, 0.960514274353782, 0.8802020889934468, 0.9420024447650903, - 0.32617198465449837, 0.26334781459438883, 0.9708661080259163 }, - { 0.9849998857013367, 0.9294648448342805, 0.6941639825602122, 0.03962873250027643, 0.34876857441906006, - 0.7102331264346626, 0.009923227971004533, 0.004291243075037587, 0.42771821843373103, - 0.48228770307558944, 0.29210057013301804, 0.10659978052559116, 0.9511736784606385, - 0.6471401175821299, 0.08000089078354955, 0.1422609026335776 }, - { 0.2735420881837255, 0.1525514198463197, 0.46667337710664325, 0.8430422042800253, 0.662646859557516, - 0.4273484753734381, 0.9350218443876921, 0.14772485980464511, 0.35831465507175264, - 0.12425253795744928, 0.700261060470252, 0.7134654616253058, 0.628788146059543, - 0.9719792883450641, 0.06762978640988282, 0.23452033520839022 }, - { 0.38660992002326133, 0.8543958197176149, 0.2379775259300757, 0.9160890057490731, 0.3122712783494066, - 0.7118595870944922, 0.33010723519532603, 0.09124265325192515 }, - { 0.46464059988353745, 0.4182842589733563, 0.6454742040446713, 0.6751443185405566, 0.33610656116635307, - 0.47217147047337293, 0.5013136121720635, 0.8598453024444461 }, - { 0.29252431692420444, 0.5445036774354164, 0.22890028277878705, 0.7981586474384571 }, - { 0.45120803219853756, 0.005439454681410383, 0.29384818661042156, 0.5224946681120767 }, - { 0.6258267454453438, 0.9485563046471146, 0.14234788814134525, 0.6706414835656253 }, - { 0.4429692727609613, 0.9816003222145677, 0.9479665512387505, 0.13103087145529047, 0.9784096110774524, - 0.5867703787286896, 0.7145487664819216, 0.5891205516574893, 0.035029622864427123, - 0.5995473632960368, 0.9423034982217162, 0.53901575498877, 0.8050200304388183, - 0.3174779164324115, 0.7732143797781064, 0.8251941778980239, 0.7277950900374809, - 0.7736491045301819, 0.710706248482189, 0.8498095803008198, 0.8226903167987375, - 0.2884563905752452, 0.9797919189308799, 0.9737107836254583, 0.8415948491227313, - 0.41465219194912595, 0.4183035359012892, 0.2709684010948765, 0.6950743419687081, - 0.9464476910783752, 0.37199873428524965, 0.17782034671421165 }, - { 0.7864927572636758, 0.2418789922428023, 0.017035942650692304, 0.3238822138709484, 0.2964123622659881, - 0.8231199960632809, 0.22959849533167642, 0.5128280021192582, 0.826162071747244, - 0.6149754443882899, 0.05850324347630931, 0.01432072231589221, 0.40266236651645326, - 0.9357647399130745, 0.7675928523784253, 0.3251647144191495 }, - { 0.6712349879452624, 0.8768633991685569, 0.9333296436072808, 0.9719874832801729, 0.9022668072799137, - 0.8633413571017937, 0.816766840903252, 0.6261466465055312, 0.043032952718658035, - 0.5274833759436072, 0.5807849109767159, 0.5534228768710968, 0.6603172096390849, - 0.26813850011980267, 0.23236379162038068, 0.8606794281184019, 0.31203203870718943, - 0.8943901140697472, 0.5400791160983084, 0.4529979440888534, 0.15166416529920868, - 0.5656941220474122, 0.272902982304166, 0.009785280483843528, 0.36102057879912275, - 0.2307220210264832, 0.5668194194053567, 0.2515551306244589, 0.9269562373834143, - 0.4060242417117247, 0.09588656778378668, 0.4471162777527842 }, - { 0.0420959635135838, 0.10953891340341015, 0.02228427097568375, 0.3299764529754966 }, - { 0.9669732816839842, 0.13899760644982895, 0.4414297951394093, 0.8240552945282527, 0.8371972666712111, - 0.10373354060386875, 0.20045983989759142, 0.023505541760897364 }, - { 0.5200251176535067, 0.4039794590721878, 0.7590485264572412, 0.6631651631903216, 0.3989003558798062, - 0.693767248255806, 0.659729341908985, 0.20088033104573177, 0.11011106048831465, - 0.7914612301673087, 0.10174794142966948, 0.06490845280803292, 0.6044074726666192, - 0.30040990308697135, 0.6401444966694853, 0.5159688446015483 }, - { 0.9567414718212607, 0.10348104171557015, 0.25219429120273673, 0.2705602567028519, 0.579440552110363, - 0.11726486785687817, 0.6130270953780019, 0.9521949368720519 }, - { 0.42950635425979655, 0.16518745944643443, 0.13458413628474974, 0.9285861639383158, 0.5740206966788683, - 0.9746846403184233, 0.23986276321155475, 0.7791645261174039, 0.6942989577222445, - 0.10302437381407292, 0.17649726059158877, 0.44259701923012407, 0.12642636197760193, - 0.018563874910113687, 0.6702218490390617, 0.8395756519279107 }, - { 0.9837398631005024, 0.8970651242018116, 0.45841877916367335, 0.9602393019645956 }, - { 0.8525631081163496, 0.9644669729172649, 0.9583040327078675, 0.08720862736937618, 0.38057991297468763, - 0.9172629043363064, 0.21015703777716566, 0.398590750888154 }, - { 0.9553448577457966, 0.3808446826563595, 0.2234692691701734, 0.6931884449795152, 0.4776102415216825, - 0.6379769378552478, 0.18197920456077177, 0.5495763216802875 }, - { 0.2246136780598329, 0.05767747155215186, 0.34443655686657215, 0.23626373941110268, 0.2094919046714232, - 0.33148396737427854, 0.6989592823292484, 0.3075750586735163, 0.30999602181149644, - 0.9444805419325929, 0.5350272394699568, 0.10259125359638843, 0.92251474463751, - 0.5766628546939399, 0.008985724914360338, 0.6161053035242512, 0.6803430663571829, - 0.19806467700559893, 0.8496720223691024, 0.797725223954955, 0.42977026069183044, - 0.2859623992406095, 0.582898925325729, 0.5438471000543933, 0.5567522706171357, - 0.9287397533406584, 0.5315422813533052, 0.7355446639294867, 0.8141949453122773, - 0.5926503762438441, 0.5887798047494944, 0.4067989362143932 }, - { 0.2575640558985337, 0.49455586531763174, 0.23116287667094992, 0.8184204285887713, 0.5791911859886936, - 0.5561610722618936, 0.6207159235834669, 0.47523356819461293, 0.39187688553339817, - 0.11686459005876793, 0.29265769310006484, 0.46897734527879675, 0.7074845790562786, - 0.1194478327696622, 0.7293916292770758, 0.6444884178736836, 0.9230055868902729, - 0.2842944321592319, 0.2891647472388498, 0.9349759803763106, 0.7441865370192212, - 0.19074195964463103, 0.8909968206481071, 0.38663989410484945, 0.7840420007487027, - 0.09858775872012648, 0.6036847723227681, 0.883894562861853, 0.017355209220390688, - 0.17825703513265356, 0.8838845828873518, 0.44563802678121645 }, - { 0.6254574882890985, 0.3396845506691285, 0.5980555987748056, 0.8747864117436839, 0.2157196222997838, - 0.3556636249319295, 0.053806813459211233, 0.2346442931802648, 0.4796148685005919, - 0.24112694504168541, 0.7790663644847666, 0.2409912290072156, 0.22051206627906483, - 0.34180702048144307, 0.4699843195392497, 0.48928572788799174 }, - { 0.7474292432892988, 0.16858497841711506, 0.6628545680181327, 0.6366739239935207 }, - { 0.6545412531900626, 0.9031939852903598, 0.7828131393210792, 0.07194999459094109, 0.3202994846650421, - 0.5967882845856519, 0.07297696421889999, 0.32558559441289847 }, - { 0.9028548797400592, 0.6086914534870387, 0.8503682936674709, 0.08381072759650299, 0.2694175337129405, - 0.7154539330685599, 0.966551261889283, 0.6532594270377007 }, - { 0.7740125823482259, 0.0673750838824213, 0.12989631420367154, 0.0877755681264033, 0.3412272314782776, - 0.6210077405051666, 0.942184839928994, 0.6923106498188738, 0.3584424101818512, - 0.994329394089604, 0.49811988062498436, 0.20711949995802992, 0.8099216616141034, - 0.21757791554483763, 0.27855614423300834, 0.9116369981465458, 0.966439106063462, - 0.804671803881807, 0.7608633527861054, 0.4663260189301722, 0.9812388750291278, - 0.5258364881964462, 0.3721517785904038, 0.9167812394734067, 0.9391534487703991, - 0.6923921715592023, 0.49034089746468823, 0.4070031406140925, 0.7212510294366945, - 0.48610987062902467, 0.9263050142815049, 0.06288898483521521 }, - { 0.8286058511962618, 0.9479079687392229, 0.7472632147955719, 0.826400598301367 }, - { 0.19375704405770544, 0.7452524160661296, 0.6989900431000508, 0.5733421837141005, 0.03610007233720314, - 0.629437685501308, 0.10711005132092344, 0.23736617274617278, 0.47058505183620924, - 0.9474018039618406, 0.41380657435989154, 0.2797356104560911, 0.40189141715709087, - 0.7095520981699313, 0.5270923628575122, 0.2552494536444865, 0.780412379694144, - 0.19845260273689336, 0.18007580659532352, 0.9951276621564948, 0.3505004853158996, - 0.816271984040408, 0.5073348370147777, 0.7662185431411317, 0.29447235072455025, - 0.32016828363095995, 0.5678785309713512, 0.9866334325870364, 0.011830014044379333, - 0.8701512387695588, 0.11728005917738937, 0.837656114582156 }, - { 0.3361744018167824, 0.5488182808161421, 0.2935753360366993, 0.36922806727087, 0.37182055302205785, - 0.5528849186874375, 0.8736774384863598, 0.6420927398375746, 0.431418272383705, - 0.8664139196917229, 0.9245370577416686, 0.7188260085556081, 0.2436988596361528, - 0.20032224181133174, 0.9188259610602927, 0.7837351503328618, 0.14429063324740077, - 0.802472962121198, 0.38298524453499827, 0.9871655455479117, 0.796096414779996, - 0.7821577119408007, 0.7505212770860117, 0.624075838277507, 0.6798108128393442, - 0.9848602164012445, 0.9567333846583475, 0.5780032613282218, 0.32043447337355746, - 0.8090405537644711, 0.8502388833490756, 0.6349005912038598 }, - { 0.9179840062514263, 0.7448040392988351, 0.871309600449137, 0.7649258038573322, 0.716100362833065, - 0.9148935664681578, 0.36841176665067554, 0.06931652410814748, 0.7413457445431846, - 0.4517734106523168, 0.2610255176116475, 0.3345233658972291, 0.40717712479269064, - 0.2818599520541738, 0.9284748949126728, 0.21060462735027263, 0.5435696081303083, - 0.21541396526874246, 0.048166909822641824, 0.034400237572296044, 0.8615299527849822, - 0.4141675345196427, 0.4333206313477377, 0.06532590436538288, 0.2901270916499121, - 0.7036385858160966, 0.8271996390451494, 0.719691017395964, 0.07851349092069604, - 0.05523162021177064, 0.13163226264668004, 0.6533387611647784 } - }; + // Define the input data array (complex IFFT inputs) + double[][] inputData = { + {0.17768499045697306, 0.3405035491673728, 0.9272906450602005, 0.28247504052271843, 0.42775517613102865, + 0.8338783039818357, 0.9813749624557385, 0.47224489612381737, 0.7936831995784907, 0.5584182145651306, + 0.5296113722056018, 0.6687593295928902, 0.9630598447622862, 0.7130539473424196, 0.860081483892192, + 0.8985058305053549}, + {0.5574117454125004, 0.5153534964811869, 0.2218520797445649, 0.6278331124271054, 0.5264697956167971, + 0.9311289722152635, 0.13031360760348354, 0.6303629910772452}, + {0.8630369856754253, 0.9497763465868887, 0.9997691100440745, 0.3303206858594411, 0.9418293674995452, + 0.836702533317602, 0.42360226191376127, 0.28470444389038796, 0.18653359615821063, 0.6164549770372061, + 0.22800778967348623, 0.8045503278555484, 0.7141418111594348, 0.8898346433405011, 0.5648709181981051, + 0.7063388262940075, 0.33464462295280606, 0.5338979798606405, 0.3604790551977437, 0.4551044306632025, + 0.023102489774489032, 0.5939484248343428, 0.601633008560747, 0.5056641001742173, 0.4945185946051195, + 0.3632887663114238, 0.09711935047598264, 0.8610061039014398, 0.1695426000726007, 0.5420844999569931, + 0.22423519306858575, 0.8138053316534443}, + {0.2668982957216445, 0.6518643198476277, 0.38293047888504705, 0.5276109887708621, 0.800891602234696, + 0.665489651348857, 0.5102919050998651, 0.03294208149274114}, + {0.8673593019099284, 0.5449051089951431, 0.35927077618823344, 0.9657806185363754}, + {0.3275052635434117, 0.13324953722224242, 0.9813054584386846, 0.41940627067946346, 0.5854578390799309, + 0.9370081210453547, 0.4965403182131879, 0.40441779810528955, 0.5902823763509569, 0.2816096727771966, + 0.9187065056993756, 0.01750173716395509, 0.9430939103428495, 0.524796393731078, 0.6522622245916397, + 0.7191101906183052, 0.924218305299526, 0.41557703109579636, 0.27205034406713446, 0.9913812499805844, + 0.8474192033613039, 0.7090188824891868, 0.6353901388073759, 0.9455732252453851, 0.5575374113914232, + 0.8165674671938592, 0.9677824877594866, 0.4330735440317255, 0.9448510262521692, 0.4022170497451182, + 0.6565890617824428, 0.3310258105072468}, + {0.7856782800988511, 0.6679548733836229, 0.143535331535097, 0.7943042164876586}, + {0.04873326306922765, 0.15565317347954144, 0.25192661757985957, 0.4991677708509388, 0.3066370428993622, + 0.16093416033095542, 0.7306599702801884, 0.3010950973671792, 0.8167182072302016, 0.35620787574024504, + 0.08043055169163749, 0.3186418802351839, 0.20232598085979203, 0.9823046248429211, 0.1332517204586674, + 0.6984885530480028, 0.2197570335512251, 0.3823643227597068, 0.2993821619426701, 0.7890734732591685, + 0.9150357080758154, 0.814073309530792, 0.6466925662323889, 0.21367632233614853, 0.9317957911629086, + 0.902898345030541, 0.6272897679127241, 0.5146060996086259, 0.47521808342344796, 0.11893945126711991, + 0.8681215624426699, 0.8562757163749677}, + {0.2813501281398796, 0.423403543513415, 0.06307283742785219, 0.1410750085202912, 0.4171503325036342, + 0.4946182389939746, 0.6531869179899719, 0.10773798427753578}, + {0.3042057982656987, 0.36010750174274897, 0.9210922929503627, 0.9192485611716222, 0.5144480274153918, + 0.5814672279115486, 0.6011866421864879, 0.7012821521290435}, + {0.9995986452888619, 0.18328112493260273, 0.38935455191203494, 0.3509391406504685, 0.3263088507735412, + 0.9648828416159605, 0.8423623129564872, 0.3871067276945551, 0.45097017756014, 0.5159198092355896, + 0.954050553063797, 0.5014833734710475, 0.7976092097295954, 0.43190395198337117, 0.12323995739627724, + 0.04493903174415104}, + {0.07843773812596688, 0.7221954247862206, 0.5131226646525481, 0.9489126584469372, 0.47532447735279626, + 0.5050483053898411, 0.11285163895281858, 0.6442680036899163, 0.6943999719718427, 0.11854357006570826, + 0.9433634243731835, 0.8471613997721141, 0.9645297215939213, 0.9946433175329309, 0.9445985285947038, + 0.07499800505216225}, + {0.025606890585376463, 0.12419720934757339, 0.6863742021872193, 0.1591793804631091, 0.20073179883607617, + 0.9163422318494329, 0.8255291764015206, 0.24839917554927815, 0.9017306407233789, 0.011143738439888695, + 0.9499466038149739, 0.23839793578917512, 0.7530603927443468, 0.7221433441575709, 0.9882231809584526, + 0.010882536537176746, 0.6372138133730175, 0.9850167826732869, 0.6096297429504447, 0.6421948734889896, + 0.4673898464886783, 0.8286049778392416, 0.7412286660738077, 0.7568564633736149, 0.512257077801464, + 0.7639553542893256, 0.019409959095512463, 0.44888036476478865, 0.5561388696313153, 0.4010303093814831, + 0.3519885049606841, 0.36180934217779015}, + {0.42957497934079614, 0.3494574872550452, 0.28432737366460115, 0.6512952245877861}, + {0.9004930907404661, 0.24177853554090678, 0.2965518122533567, 0.15880453298324915, 0.808380071501632, + 0.534445489902122, 0.6553342025721992, 0.3775806858178651}, + {0.05039928002703009, 0.6153252657122776, 0.18969212975794214, 0.6401253643448036, 0.6169843888554394, + 0.8312875187886041, 0.9490105226327502, 0.7245238257008109}, + {0.05875630673478127, 0.02409603626667045, 0.8073637232586641, 0.5978959407049632, 0.673078922215489, + 0.5196326131440167, 0.4738950710992147, 0.032500830859891416, 0.8810220711016585, 0.12909173283303255, + 0.9213328273665816, 0.8090874942954726, 0.9354909447748617, 0.882576819693844, 0.04917162075490544, + 0.49253892564640533, 0.3898160399036703, 0.13157304806755854, 0.6997084267185423, 0.23507481777217032, + 0.07693269358017651, 0.41923240484513036, 0.7701296835014443, 0.9230307316770667, 0.44276343787221584, + 0.4556723674991543, 0.0005269550147345425, 0.9469237088461168, 0.43039381632701756, 0.9818691560857561, + 0.0032385749365969607, 0.38460732810872156}, + {0.07177251361521142, 0.597972082172478, 0.6226088866570731, 0.7447140422117096, 0.03608317332120958, + 0.9403392994904969, 0.4214767886988283, 0.5400819129156715}, + {0.7886913760270524, 0.2811741704130385, 0.5337400451604049, 0.8427268522792017, 0.43468289560698725, + 0.4474995478449879, 0.4384795264165595, 0.9811467374329375, 0.07392345242810194, 0.1727386149139265, + 0.9717684346647587, 0.9363308310478798, 0.5655367186251736, 0.7638950609861564, 0.7938449219818952, + 0.1853731675532243}, + {0.8599654952652722, 0.9067031377676662, 0.9192926722382764, 0.7337945168999715, 0.13609124919880633, + 0.9797115711637998, 0.3065888944026701, 0.1660206808739042, 0.27230041646280134, 0.3066077606192541, + 0.2953550472086268, 0.921215997827601, 0.9959872895290139, 0.7446027654086131, 0.32904544352394216, + 0.3278728444599871}, + {0.7347977442629331, 0.2823105544640442, 0.06972698993685245, 0.024892539689898574, 0.8377112725721335, + 0.795264231632406, 0.9831660948336206, 0.6726586022412739, 0.23734748257972293, 0.27596016754533936, + 0.960514274353782, 0.8802020889934468, 0.9420024447650903, 0.32617198465449837, 0.26334781459438883, + 0.9708661080259163}, + {0.9849998857013367, 0.9294648448342805, 0.6941639825602122, 0.03962873250027643, 0.34876857441906006, + 0.7102331264346626, 0.009923227971004533, 0.004291243075037587, 0.42771821843373103, + 0.48228770307558944, 0.29210057013301804, 0.10659978052559116, 0.9511736784606385, 0.6471401175821299, + 0.08000089078354955, 0.1422609026335776}, + {0.2735420881837255, 0.1525514198463197, 0.46667337710664325, 0.8430422042800253, 0.662646859557516, + 0.4273484753734381, 0.9350218443876921, 0.14772485980464511, 0.35831465507175264, 0.12425253795744928, + 0.700261060470252, 0.7134654616253058, 0.628788146059543, 0.9719792883450641, 0.06762978640988282, + 0.23452033520839022}, + {0.38660992002326133, 0.8543958197176149, 0.2379775259300757, 0.9160890057490731, 0.3122712783494066, + 0.7118595870944922, 0.33010723519532603, 0.09124265325192515}, + {0.46464059988353745, 0.4182842589733563, 0.6454742040446713, 0.6751443185405566, 0.33610656116635307, + 0.47217147047337293, 0.5013136121720635, 0.8598453024444461}, + {0.29252431692420444, 0.5445036774354164, 0.22890028277878705, 0.7981586474384571}, + {0.45120803219853756, 0.005439454681410383, 0.29384818661042156, 0.5224946681120767}, + {0.6258267454453438, 0.9485563046471146, 0.14234788814134525, 0.6706414835656253}, + {0.4429692727609613, 0.9816003222145677, 0.9479665512387505, 0.13103087145529047, 0.9784096110774524, + 0.5867703787286896, 0.7145487664819216, 0.5891205516574893, 0.035029622864427123, 0.5995473632960368, + 0.9423034982217162, 0.53901575498877, 0.8050200304388183, 0.3174779164324115, 0.7732143797781064, + 0.8251941778980239, 0.7277950900374809, 0.7736491045301819, 0.710706248482189, 0.8498095803008198, + 0.8226903167987375, 0.2884563905752452, 0.9797919189308799, 0.9737107836254583, 0.8415948491227313, + 0.41465219194912595, 0.4183035359012892, 0.2709684010948765, 0.6950743419687081, 0.9464476910783752, + 0.37199873428524965, 0.17782034671421165}, + {0.7864927572636758, 0.2418789922428023, 0.017035942650692304, 0.3238822138709484, 0.2964123622659881, + 0.8231199960632809, 0.22959849533167642, 0.5128280021192582, 0.826162071747244, 0.6149754443882899, + 0.05850324347630931, 0.01432072231589221, 0.40266236651645326, 0.9357647399130745, 0.7675928523784253, + 0.3251647144191495}, + {0.6712349879452624, 0.8768633991685569, 0.9333296436072808, 0.9719874832801729, 0.9022668072799137, + 0.8633413571017937, 0.816766840903252, 0.6261466465055312, 0.043032952718658035, 0.5274833759436072, + 0.5807849109767159, 0.5534228768710968, 0.6603172096390849, 0.26813850011980267, 0.23236379162038068, + 0.8606794281184019, 0.31203203870718943, 0.8943901140697472, 0.5400791160983084, 0.4529979440888534, + 0.15166416529920868, 0.5656941220474122, 0.272902982304166, 0.009785280483843528, 0.36102057879912275, + 0.2307220210264832, 0.5668194194053567, 0.2515551306244589, 0.9269562373834143, 0.4060242417117247, + 0.09588656778378668, 0.4471162777527842}, + {0.0420959635135838, 0.10953891340341015, 0.02228427097568375, 0.3299764529754966}, + {0.9669732816839842, 0.13899760644982895, 0.4414297951394093, 0.8240552945282527, 0.8371972666712111, + 0.10373354060386875, 0.20045983989759142, 0.023505541760897364}, + {0.5200251176535067, 0.4039794590721878, 0.7590485264572412, 0.6631651631903216, 0.3989003558798062, + 0.693767248255806, 0.659729341908985, 0.20088033104573177, 0.11011106048831465, 0.7914612301673087, + 0.10174794142966948, 0.06490845280803292, 0.6044074726666192, 0.30040990308697135, 0.6401444966694853, + 0.5159688446015483}, + {0.9567414718212607, 0.10348104171557015, 0.25219429120273673, 0.2705602567028519, 0.579440552110363, + 0.11726486785687817, 0.6130270953780019, 0.9521949368720519}, + {0.42950635425979655, 0.16518745944643443, 0.13458413628474974, 0.9285861639383158, 0.5740206966788683, + 0.9746846403184233, 0.23986276321155475, 0.7791645261174039, 0.6942989577222445, 0.10302437381407292, + 0.17649726059158877, 0.44259701923012407, 0.12642636197760193, 0.018563874910113687, 0.6702218490390617, + 0.8395756519279107}, + {0.9837398631005024, 0.8970651242018116, 0.45841877916367335, 0.9602393019645956}, + {0.8525631081163496, 0.9644669729172649, 0.9583040327078675, 0.08720862736937618, 0.38057991297468763, + 0.9172629043363064, 0.21015703777716566, 0.398590750888154}, + {0.9553448577457966, 0.3808446826563595, 0.2234692691701734, 0.6931884449795152, 0.4776102415216825, + 0.6379769378552478, 0.18197920456077177, 0.5495763216802875}, + {0.2246136780598329, 0.05767747155215186, 0.34443655686657215, 0.23626373941110268, 0.2094919046714232, + 0.33148396737427854, 0.6989592823292484, 0.3075750586735163, 0.30999602181149644, 0.9444805419325929, + 0.5350272394699568, 0.10259125359638843, 0.92251474463751, 0.5766628546939399, 0.008985724914360338, + 0.6161053035242512, 0.6803430663571829, 0.19806467700559893, 0.8496720223691024, 0.797725223954955, + 0.42977026069183044, 0.2859623992406095, 0.582898925325729, 0.5438471000543933, 0.5567522706171357, + 0.9287397533406584, 0.5315422813533052, 0.7355446639294867, 0.8141949453122773, 0.5926503762438441, + 0.5887798047494944, 0.4067989362143932}, + {0.2575640558985337, 0.49455586531763174, 0.23116287667094992, 0.8184204285887713, 0.5791911859886936, + 0.5561610722618936, 0.6207159235834669, 0.47523356819461293, 0.39187688553339817, 0.11686459005876793, + 0.29265769310006484, 0.46897734527879675, 0.7074845790562786, 0.1194478327696622, 0.7293916292770758, + 0.6444884178736836, 0.9230055868902729, 0.2842944321592319, 0.2891647472388498, 0.9349759803763106, + 0.7441865370192212, 0.19074195964463103, 0.8909968206481071, 0.38663989410484945, 0.7840420007487027, + 0.09858775872012648, 0.6036847723227681, 0.883894562861853, 0.017355209220390688, 0.17825703513265356, + 0.8838845828873518, 0.44563802678121645}, + {0.6254574882890985, 0.3396845506691285, 0.5980555987748056, 0.8747864117436839, 0.2157196222997838, + 0.3556636249319295, 0.053806813459211233, 0.2346442931802648, 0.4796148685005919, 0.24112694504168541, + 0.7790663644847666, 0.2409912290072156, 0.22051206627906483, 0.34180702048144307, 0.4699843195392497, + 0.48928572788799174}, + {0.7474292432892988, 0.16858497841711506, 0.6628545680181327, 0.6366739239935207}, + {0.6545412531900626, 0.9031939852903598, 0.7828131393210792, 0.07194999459094109, 0.3202994846650421, + 0.5967882845856519, 0.07297696421889999, 0.32558559441289847}, + {0.9028548797400592, 0.6086914534870387, 0.8503682936674709, 0.08381072759650299, 0.2694175337129405, + 0.7154539330685599, 0.966551261889283, 0.6532594270377007}, + {0.7740125823482259, 0.0673750838824213, 0.12989631420367154, 0.0877755681264033, 0.3412272314782776, + 0.6210077405051666, 0.942184839928994, 0.6923106498188738, 0.3584424101818512, 0.994329394089604, + 0.49811988062498436, 0.20711949995802992, 0.8099216616141034, 0.21757791554483763, 0.27855614423300834, + 0.9116369981465458, 0.966439106063462, 0.804671803881807, 0.7608633527861054, 0.4663260189301722, + 0.9812388750291278, 0.5258364881964462, 0.3721517785904038, 0.9167812394734067, 0.9391534487703991, + 0.6923921715592023, 0.49034089746468823, 0.4070031406140925, 0.7212510294366945, 0.48610987062902467, + 0.9263050142815049, 0.06288898483521521}, + {0.8286058511962618, 0.9479079687392229, 0.7472632147955719, 0.826400598301367}, + {0.19375704405770544, 0.7452524160661296, 0.6989900431000508, 0.5733421837141005, 0.03610007233720314, + 0.629437685501308, 0.10711005132092344, 0.23736617274617278, 0.47058505183620924, 0.9474018039618406, + 0.41380657435989154, 0.2797356104560911, 0.40189141715709087, 0.7095520981699313, 0.5270923628575122, + 0.2552494536444865, 0.780412379694144, 0.19845260273689336, 0.18007580659532352, 0.9951276621564948, + 0.3505004853158996, 0.816271984040408, 0.5073348370147777, 0.7662185431411317, 0.29447235072455025, + 0.32016828363095995, 0.5678785309713512, 0.9866334325870364, 0.011830014044379333, 0.8701512387695588, + 0.11728005917738937, 0.837656114582156}, + {0.3361744018167824, 0.5488182808161421, 0.2935753360366993, 0.36922806727087, 0.37182055302205785, + 0.5528849186874375, 0.8736774384863598, 0.6420927398375746, 0.431418272383705, 0.8664139196917229, + 0.9245370577416686, 0.7188260085556081, 0.2436988596361528, 0.20032224181133174, 0.9188259610602927, + 0.7837351503328618, 0.14429063324740077, 0.802472962121198, 0.38298524453499827, 0.9871655455479117, + 0.796096414779996, 0.7821577119408007, 0.7505212770860117, 0.624075838277507, 0.6798108128393442, + 0.9848602164012445, 0.9567333846583475, 0.5780032613282218, 0.32043447337355746, 0.8090405537644711, + 0.8502388833490756, 0.6349005912038598}, + {0.9179840062514263, 0.7448040392988351, 0.871309600449137, 0.7649258038573322, 0.716100362833065, + 0.9148935664681578, 0.36841176665067554, 0.06931652410814748, 0.7413457445431846, 0.4517734106523168, + 0.2610255176116475, 0.3345233658972291, 0.40717712479269064, 0.2818599520541738, 0.9284748949126728, + 0.21060462735027263, 0.5435696081303083, 0.21541396526874246, 0.048166909822641824, + 0.034400237572296044, 0.8615299527849822, 0.4141675345196427, 0.4333206313477377, 0.06532590436538288, + 0.2901270916499121, 0.7036385858160966, 0.8271996390451494, 0.719691017395964, 0.07851349092069604, + 0.05523162021177064, 0.13163226264668004, 0.6533387611647784}}; - double[][][] outputData = { - { { 0.5554009454874607, 0.017189765873370448, -0.12592905534215526, -0.011757491289470089, - 0.07312549803852456, 0.0029102156297631868, -0.19987730488982902, -0.13337758305069133 }, - { 0.7481466528055458, -0.08167572695583827, 0.098339013093207, -0.0814324809790821, - 0.03846232230409691, 0.02581048631100489, -0.006576466032461176, - 0.052609399032017695 } }, - { { 0.48061260851633936, 0.008698421132479317, -0.09098069593780675, 0.15908141170148843 }, - { 0.5545688416281973, 0.07091914301684876, -0.226177140018057, 0.127158950989808 } }, - { { 0.6462796640314765, 0.1207206107818119, -0.01762005486206572, 0.006306293048971567, - 0.06822824850575576, -0.003497250886612027, -0.03677191329183099, 0.09200845566382201, - -0.031055683991221217, 0.006213039230073512, -0.012146295246651465, -0.07738509259143851, - -0.007066788422857184, 0.08229947582855819, -0.08506188580578779, 0.1115861636834207 }, - { 0.43587965950398616, 0.01119474204674489, 0.0436034631759588, 0.054078336398618976, - 0.056574744780298086, -0.04895519013390881, 0.03450482991652083, -0.10638546065835308, - -0.14772029516547686, 0.0429305206422676, 0.0658742677386075, -0.03671568243477094, - -0.08928203226755357, 0.01178332361684557, 0.015146971096621827, - -0.007867575303600903 } }, - { { 0.45732602080629536, -0.1871449382548796, -0.1324116335029496, 0.1291288466731783 }, - { 0.5024038100440398, 0.10371325705289913, 0.15318794362324079, 0.041586591514516325 } }, - { { 0.7061322054525357, 0.16122709645739264 }, { 0.6625256973623044, -0.303254921174071 } }, - { { 0.5582658511001826, -0.014704840584211537, -0.0627157060618794, 0.012456193354444395, - -0.01530010739904733, -0.04788008169393359, -0.03738544216219424, -0.10896799274342381, - 0.128628385932322, 0.07670205169968633, -0.007111511533772388, -0.013410977950846295, - -0.06000928230416999, -0.055453451900711194, -0.045478367624256937, 0.01987054341522309 }, - { 0.6781420149381103, -0.0500509217664482, -0.05026096269054299, -0.014056755596229473, - 0.06615260263105256, 0.0270935493730691, -0.1417803225644949, 0.10813300914922121, - 0.047587732401997485, -0.01667033621629791, 0.10534782624188156, 0.15598626905541169, - 0.026624136604945267, 0.04188891427097305, 0.009064830782525346, - -0.06898328131564806 } }, - { { 0.726816576741237, 0.05886170335761409 }, { 0.4689197740113778, -0.3253844424762808 } }, - { { 0.37769853062274406, -0.06287167118587406, 0.013272760755422566, 0.059798661777511176, - 0.020843863564936352, 0.06255457992886422, -0.020505282720239305, -0.12710747661681882, - -0.056363111364126944, -0.07719436952748313, 0.10480606991456991, -0.014618947965474575, - 0.0014243406910924988, -0.2244391814188424, -0.008451436314684419, -0.00011406707236942537 }, - { 0.5984499821819326, -0.06302615132947265, -0.07883953880314987, -0.004324658112628804, - -0.003873306839043633, -0.11366765114650743, -0.022871220314031784, - -0.13800116659867828, 0.024461602161048757, 0.04561090297164721, -0.017442397228411198, - -0.020345419001976284, 0.016413376549411607, -0.020849024388695465, 0.05947791464931039, - -0.041416211199530026 } }, - { { 0.2272253794003595, -0.04215074100110286, -0.05501389661649361, 0.15128938635711656 }, - { 0.41817336844127917, 0.011572987376696542, 0.11699525680552395, -0.12959128011986537 } }, - { { 0.6261635385326081, -0.12426789261679225, -0.01351449292457746, -0.18417535472553972 }, - { 0.599596012410618, -0.16146991854999235, -0.04177867760967813, 0.11810061116444429 } }, - { { 0.555479274478064, -0.13335701393571267, -0.03840134060123515, 0.20612101698012242, - 0.08392681575466732, 0.09397681364766289, 0.061948998399705385, 0.1699040805655877 }, - { 0.4775145080229961, -0.20516430386037218, 0.07267587187915008, -0.026058009209961, - 0.10395296641445628, 0.005252605556895273, -0.02985365267173483, - 0.05265019142871023 } }, - { { 0.5000201139246306, -0.04800345446956701, -0.0329049463626208, -0.03284470537729227, - -0.20508598415359822, -0.05090945428176026, 0.01485192433096992, -0.06668575548479515 }, - { 0.6977797423695709, -0.08329928338393976, -0.07437114892036442, 0.10800747144622731, - 0.18894316926384197, 0.11583460240335249, 0.017113084069833556, - -0.27560766527667935 } }, - { { 0.4851180273990343, -0.16126949568571958, -0.06691674009092896, -0.10704718961757465, - -0.14611311380319633, -0.035002563984131076, 0.02014526618951898, -0.05833761506438867, - 0.18128233338238373, -0.03264166614264144, 0.12163209118053736, -0.09121827833544369, - -0.05000481625592715, 0.03207004406365072, -0.08147428234704425, 0.015384889697247134 }, - { 0.5677253092727153, -0.03729235153062347, -0.039365455693876845, 0.04146785092046644, - 0.09798188935435953, -0.008627080611540536, -0.06552852244983984, 0.10072436882505036, - -0.08081824922584976, -0.03604185729698669, 0.03292928365596532, -0.0122484248012707, - -0.04163904757760628, -0.02488167514502858, 0.10345023825137331, - 0.039377537425709944 } }, - { { 0.3895162332979207, 0.04005874604287546 }, { 0.4678112991261936, -0.18348392546159248 } }, - { { 0.39940699287949466, 0.11176911860071312, 0.19911545861741672, 0.1902015206428416 }, - { 0.5939351124484545, 0.05900496787177262, 0.137922024588461, 0.0175179665929438 } }, - { { 0.3738855099605134, -0.061514135704676315, -0.2538398050680273, -0.00813228916077971 }, - { 0.7804515639944012, -0.0892065581024592, 0.002545891749693663, -0.07680650878619621 } }, - { { 0.5179707425469033, -0.025302819300953475, -0.08988468070186949, -0.15759688083449927, - 0.04986716326586882, 0.016711033222231685, 0.0893262152057811, 0.011837069383857, - 0.08204319336636628, -0.21049563858248832, 0.015427354145726171, -0.07133856817868453, - -0.01279403797244072, 0.10188626425620104, -0.18206676093811552, -0.07683334214910278 }, - { 0.4557183244222546, -0.10900814441739004, 0.04904230378999857, -0.0010906845210327046, - -0.031895227628587916, -0.037331861262286915, -0.040831099380204676, - 0.0030723082051012374, -0.10402962094045479, 0.09107053711775343, 0.14231804953997862, - 0.06072556870526918, 0.015183021067558186, -0.023570386570056008, -0.0692160119825995, - -0.010341036241630921 } }, - { { 0.509266881164118, -0.23777343990417177, -0.16207618102797572, -0.037644746616759095 }, - { 0.4844952936065516, -0.13303389385421258, -0.25571531259653263, -0.05966291383459679 } }, - { { 0.5935176438976462, 0.005419535054208113, 0.05452812784476206, 0.05483345393049244, - -0.04461918309489521, 0.03860170688009229, 0.008260547169506763, 0.07814954434523974 }, - { 0.5579264002751396, -0.19510727214536616, -0.2776691321309364, 0.01833209516889496, - 0.043341981649842765, 0.09601908528205956, -0.003869164267408187, - -0.16505054140412423 } }, - { { 0.6260210272262958, 0.02432692813809728, -0.003993313239643373, 0.12917970105240434, - -0.07053644945003956, 0.16506423245734803, -0.05346289230457359, 0.04336626138538329 }, - { 0.5241234456299799, -0.061299666157965914, 0.20381084080210454, -0.03215905928455756, - -0.05095139644888391, 0.03355389235031433, -0.04283903698729301, - -0.3019386034408972 } }, - { { 0.5500660037041453, -0.0756423721419075, 0.2210689971105149, 0.07481755482487502, - 0.10628452169723956, -0.1243776248752409, -0.09116501409436648, 0.07374567803767317 }, - { 0.6070515456890231, -0.3012803549751432, 0.04193893532024, -0.08007158532442629, - -0.006248541615777148, -0.10324316179539066, -0.05306697572107942, - 0.1322676210022765 } }, - { { 0.4651842021869838, 0.08699362121518653, -0.03136073598867131, 0.10751032697033988, - 0.04427971547591952, 0.019039286768015494, 0.18878104838596632, 0.1045724206875964 }, - { 0.39116023270347816, 0.031180108596409038, 0.32532105395892885, -0.11704207031957495, - 0.04658810674925612, 0.009016215044166007, -0.07362344496447837, - -0.1848819833344538 } }, - { { 0.4885688910675007, -0.18086781613949704, -0.07671903790273893, 0.14878362006771667, - 0.09590215124139359, -0.07456619521904288, -0.03965753053553456, -0.08790199439607198 }, - { 0.474901408893455, -0.17244598635312056, -0.023956901826718974, 0.1791658005916335, - -0.036152996890597366, -0.012259503214089251, 0.07875989038950917, - -0.12969705651831892 } }, - { { 0.5987680678550062, -0.11799613493734534, -0.2864743448783378, 0.19231233198393816 }, - { 0.36137018847278746, -0.019882285719344417, -0.04018093170042117, 0.010964307296384701 } }, - { { 0.5508858453605304, 0.051710056952484845, 0.004171556603573934, -0.14212685903305175 }, - { 0.5423592365640588, -0.10551677764322767, -0.1236491498948506, 0.02291325214037246 } }, - { { 0.4185139971798104, -0.12598968025560597 }, { 0.5135294651086221, -0.28462918232983503 } }, - { { 0.22832374343997397, 0.2228842887585636 }, { 0.4081714273612491, -0.11432324075082756 } }, - { { 0.7871915250462291, -0.1613647796008854 }, { 0.40649468585348525, -0.26414679771214 } }, - { { 0.6380761918458395, -0.022978944297255756, -0.0014396124781275166, -0.02384143615782447, - -0.07921880781102483, -0.06801029020723776, -0.15490186756072524, 0.03355342032660816, - 0.06685652476192966, -0.01586151971323415, -0.13389162215415135, 0.005533156811150963, - -0.060356774511329564, 0.17693167298435386, -0.03612458427971646, 0.11864376520170625 }, - { 0.6414668453372225, -0.012718518336902778, 0.024705495293850604, -0.07647416157394636, - 0.06296179906251137, 0.11361886055723146, -0.02420694444897769, -0.03841719639006607, - 0.05452753410368566, -0.01239440538295344, 0.032061027655300084, 0.06376752807408094, - 0.012832470978494881, -0.07360848144902933, -0.01965325840198135, - -0.020673505041039646 } }, - { { 0.4039060952260403, 0.17105094720582825, -0.04687300825237843, 0.06312719676982903, - -0.07152120584803218, 0.12874155376912266, 0.2559406786392022, -0.1178795002459361 }, - { 0.4931432693943547, -0.04258744279908336, 0.07887713934060492, 0.012311293243742596, - 0.020586864135253208, 0.09532173093653501, 0.021804946261635733, 0.1467042712342011 } }, - { { 0.6492600132374694, 0.07405704598578203, -0.10652150413678427, 0.014145446840794215, - -0.0763605684643836, 0.07600883542245865, -0.03496573571710465, -0.06855537550667139, - -0.04424787015115095, 0.06760348676948996, -0.09178162857206457, -0.025320785111737663, - 0.040561414773794824, 0.13320415864997182, 0.021189849362183932, 0.042958204563214517 }, - { 0.40535288984911627, 0.17032068199517506, 0.01331219400387537, 0.03853473160782414, - -0.012526554240257781, -0.06187945408577552, -0.036479509907725347, - -0.07188532577134454, -0.0019327516265471578, -0.08139500313648848, - -0.0058851768933687165, 0.025922987062677243, 0.047024671064922474, 0.0211940396143128, - -0.072339453496859, -0.06530692733234739 } }, - { { 0.07581743845849698, -0.03372147494491318 }, { 0.17613036197559018, -0.15384609099990643 } }, - { { 0.5928639944503687, 0.11132887192540089, 0.11133754396132797, 0.15144287134688658 }, - { 0.2912240472333921, -0.012080065326201012, 0.2276045060510091, 0.3304487787130108 } }, - { { 0.5374369429329483, 0.0124309756250367, -0.1263557788347015, 0.010780749436562376, - 0.0469888925419365, 0.15244935362834236, 0.0013926801264731864, -0.1150986978030912 }, - { 0.39114492523974376, 0.049146273279162184, 0.02579091351846495, -0.14222691912065577, - -0.027042182426221584, -0.14789058018667428, -0.03263438975452017, - -0.0061769800609844105 } }, - { { 0.39574426536060486, 0.3848693124084244, 0.20872361615139384, -0.03259572209916245 }, - { 0.5654818630543237, -0.05016643956373015, 0.030751960689858693, 0.03337316792991071 } }, - { { 0.5281995925319434, -0.013483007703420306, 0.22370807173452606, 0.032600345010997386, - -0.183706104923201, 0.10078556921052059, -0.06643803387393597, -0.19216007772763355 }, - { 0.3839006686515898, 0.04203486886439816, -0.07423404752770826, -0.0167526930749399, - 0.03296043868103443, 0.07361362334006122, 0.06773560004500725, 0.1850404987428018 } }, - { { 0.940402493651157, 0.04333736944934541 }, { 0.7093290405641345, -0.25091026140046113 } }, - { { 0.7156356852777146, -0.15610326950991757, 0.189797885134394, 0.10323280721415865 }, - { 0.4766476514940784, 0.2619203051863527, -0.18127917611815178, -0.1767088675875917 } }, - { { 0.5632118136379611, 0.16086874310016572, 0.026195249820023803, 0.20506905118764587 }, - { 0.46178567640449736, -0.004178181340561221, -0.1319909533632702, 0.1519936998210166 } }, - { { 0.4016790839699139, -0.03003822458401474, -0.057149866644219526, -0.011524063301240554, - 0.034856641495165167, 0.13554174060998003, -0.09095916185362438, -0.0018328637444979812, - 0.005074060125136143, -0.03801226191198792, -0.043716698741379005, 0.0030306419969509946, - -0.024955698295149528, 0.007269331103218465, 0.042476489879821895, -0.10712547204424006 }, - { 0.5952054191724998, -0.07676059511106324, -0.026180524219684337, -0.04836907409057477, - 0.03599606184553125, -0.09752988130462281, -0.018060463584829087, 0.050791615691301736, - 0.034038777924507435, 0.07585998009242355, 0.04676163922757573, 0.16844664234113793, - -0.044975123197931816, -0.04892751473324741, -0.004238118680509565, - 0.03828422498466859 } }, - { { 0.46901212184076757, -0.01535709462655225, -0.03916243436874867, 0.060857712029923704, - 0.1225909913945583, -0.09340926175888574, -0.1289223230381446, -0.010663880940625405, - 0.00724348179779008, -0.060944417834272645, 0.06976206691384879, 0.03620812577362522, - -0.11481741841389, -0.045575265138713114, -0.060986015410215574, 0.061727667678068 }, - { 0.5337093691722835, 0.04436500074224131, -0.030963373409186423, 0.020253982238142385, - -0.0824519992966499, 0.012010153722473894, 0.15829867840518805, 0.04533676610748462, - 0.1083306629496745, -0.08650086214010269, 0.045865730697915885, 0.026540593157353194, - 0.05755930064433876, 0.03279325594388378, 0.06317542465592338, - -0.025317096700691388 } }, - { { 0.4122273004184882, -0.014566204465011313, 0.04208221121586804, 0.17869124295327457, - -0.038967419712763435, 0.03973015972596076, 0.005246463372848328, 0.0010137347804333452 }, - { 0.4077985676527511, 0.1686350835680268, -0.12037578482090629, 0.006478097285106572, - 0.07949583704816715, 0.032202813316253534, -0.016855152490183614, - -0.0777645930586234 } }, - { { 0.45800711085320694, 0.2894221324360919 }, { 0.6497642460058267, 0.013090322012305977 } }, - { { 0.6031245930981106, -0.09986864407594251, 0.11555260315746024, 0.035732701010434215 }, - { 0.3289125819706231, 0.2696416277863902, -0.13227435752865208, -0.14598036756331914 } }, - { { 0.6114313386227679, -0.002426979989567729, 0.2651802480809971, 0.028670273025861864 }, - { 0.651170538927121, -0.04306325057145169, -0.03318614112600926, -0.3055036135167195 } }, - { { 0.4957183696678124, -0.10553486119506748, 0.052758247615336755, 0.06020035276185396, - -0.013822765236362097, -0.02389059354934929, -0.08857441873167826, 0.008621444261494117, - 0.02082676340882711, 0.05884859157016205, -0.04918841735798534, 0.1405177719428041, - 0.0681786035653371, 0.10947244481774009, 0.08033111333375087, -0.04045006452645019 }, - { 0.6574845762838595, -0.031453441409394496, -0.04620430117138156, -0.08424489796710648, - 0.06624180213432235, -0.13120364199595552, -0.024391758054135494, 0.08573068590472949, - 0.11223336151893865, 0.07527943774180709, -0.0024984661992818805, 0.09946105848481208, - 0.06606087488780027, -0.022974547547147776, 0.12387018801680866, - 0.02304817543478708 } }, - { { 0.8882569099677424, -0.059651058771480525 }, { 0.7868319065484695, -0.03956869175289757 } }, - { { 0.45166687758041546, -0.0018649641234558647, 0.04104745825547753, -0.04331775004206736, - 0.04599538731492686, -0.03347890915900812, 0.05706572867550946, 0.04407075696339463, - -0.09550030045209212, -0.01294296290526355, -0.028171062627338407, -0.011479435442369852, - -0.126578568096198, -0.1055877835747785, -0.01335447270374341, 0.0261870443942967 }, - { 0.5375290203239034, -0.02717126518144706, 0.03769657408945279, 0.05903704597317041, - 0.10941228619740563, 0.034836047977018406, 0.11449317534415826, 0.03329000205035401, - -0.18630596238167652, -0.03847833560601357, 0.11119698020303745, 0.10426826809732498, - -0.10133153669488919, 0.06085072384786873, -0.08524817187204464, - 0.016337527326520968 } }, - { { 0.5672530754492042, -0.07621450260422805, 0.04780501932723003, 0.1360648011834046, - -0.13636811964603351, -0.05559164579848517, -0.07319574079493588, -0.022375181723106995, - -0.01803709042623941, 0.012598739926626794, 0.003834329770772228, 0.02279304289413908, - -0.06706984366225677, -0.023519044517253875, 0.05957470708250281, -0.041378144644557704 }, - { 0.6927367377783716, -0.08658190014174541, -0.044710161680140095, -0.10627851813733222, - -0.08408056627365093, 0.02806037261656114, 0.03681651148127257, -0.0438823647392234, - -0.08259784729478026, -0.0660019535766834, -0.06364239429924656, 0.005461792317948917, - -0.04090024064986589, 0.022673859550358066, -0.0015713160185880162, - -0.021211377685855435 } }, - { { 0.5615331442331852, 0.09973756929788653, -0.043561441091611794, 0.10577901659492642, - 0.027355604592400162, -0.07846639287809637, 0.06769255556633608, 0.11495862363660969, - 0.08994548302237715, -0.029898108867756554, 0.07176626712854703, -0.04707555302652763, - 0.016817577757129054, -0.14296761759104473, 0.03811568418894253, 0.0662515936881235 }, - { 0.37970420079142386, 0.035427020131707, 0.03758585103249978, 0.09252346890432356, - 0.08421133424276199, 0.0013340546210409143, 0.021541970955435447, 0.03079966148676702, - 0.022053247502089618, -0.05621550573850214, -0.07144813696100247, 0.0005035402070686645, - -0.04253374666480074, 0.16004586961594686, -0.01426637100829721, -0.1376968509881538 } } - }; + double[][][] outputData = { + {{0.5554009454874607, 0.017189765873370448, -0.12592905534215526, -0.011757491289470089, + 0.07312549803852456, 0.0029102156297631868, -0.19987730488982902, -0.13337758305069133}, + {0.7481466528055458, -0.08167572695583827, 0.098339013093207, -0.0814324809790821, 0.03846232230409691, + 0.02581048631100489, -0.006576466032461176, 0.052609399032017695}}, + {{0.48061260851633936, 0.008698421132479317, -0.09098069593780675, 0.15908141170148843}, + {0.5545688416281973, 0.07091914301684876, -0.226177140018057, 0.127158950989808}}, + {{0.6462796640314765, 0.1207206107818119, -0.01762005486206572, 0.006306293048971567, 0.06822824850575576, + -0.003497250886612027, -0.03677191329183099, 0.09200845566382201, -0.031055683991221217, + 0.006213039230073512, -0.012146295246651465, -0.07738509259143851, -0.007066788422857184, + 0.08229947582855819, -0.08506188580578779, 0.1115861636834207}, + {0.43587965950398616, 0.01119474204674489, 0.0436034631759588, 0.054078336398618976, + 0.056574744780298086, -0.04895519013390881, 0.03450482991652083, -0.10638546065835308, + -0.14772029516547686, 0.0429305206422676, 0.0658742677386075, -0.03671568243477094, + -0.08928203226755357, 0.01178332361684557, 0.015146971096621827, -0.007867575303600903}}, + {{0.45732602080629536, -0.1871449382548796, -0.1324116335029496, 0.1291288466731783}, + {0.5024038100440398, 0.10371325705289913, 0.15318794362324079, 0.041586591514516325}}, + {{0.7061322054525357, 0.16122709645739264}, {0.6625256973623044, -0.303254921174071}}, + {{0.5582658511001826, -0.014704840584211537, -0.0627157060618794, 0.012456193354444395, + -0.01530010739904733, -0.04788008169393359, -0.03738544216219424, -0.10896799274342381, + 0.128628385932322, 0.07670205169968633, -0.007111511533772388, -0.013410977950846295, + -0.06000928230416999, -0.055453451900711194, -0.045478367624256937, 0.01987054341522309}, + {0.6781420149381103, -0.0500509217664482, -0.05026096269054299, -0.014056755596229473, + 0.06615260263105256, 0.0270935493730691, -0.1417803225644949, 0.10813300914922121, + 0.047587732401997485, -0.01667033621629791, 0.10534782624188156, 0.15598626905541169, + 0.026624136604945267, 0.04188891427097305, 0.009064830782525346, -0.06898328131564806}}, + {{0.726816576741237, 0.05886170335761409}, {0.4689197740113778, -0.3253844424762808}}, + {{0.37769853062274406, -0.06287167118587406, 0.013272760755422566, 0.059798661777511176, + 0.020843863564936352, 0.06255457992886422, -0.020505282720239305, -0.12710747661681882, + -0.056363111364126944, -0.07719436952748313, 0.10480606991456991, -0.014618947965474575, + 0.0014243406910924988, -0.2244391814188424, -0.008451436314684419, -0.00011406707236942537}, + {0.5984499821819326, -0.06302615132947265, -0.07883953880314987, -0.004324658112628804, + -0.003873306839043633, -0.11366765114650743, -0.022871220314031784, -0.13800116659867828, + 0.024461602161048757, 0.04561090297164721, -0.017442397228411198, -0.020345419001976284, + 0.016413376549411607, -0.020849024388695465, 0.05947791464931039, -0.041416211199530026}}, + {{0.2272253794003595, -0.04215074100110286, -0.05501389661649361, 0.15128938635711656}, + {0.41817336844127917, 0.011572987376696542, 0.11699525680552395, -0.12959128011986537}}, + {{0.6261635385326081, -0.12426789261679225, -0.01351449292457746, -0.18417535472553972}, + {0.599596012410618, -0.16146991854999235, -0.04177867760967813, 0.11810061116444429}}, + {{0.555479274478064, -0.13335701393571267, -0.03840134060123515, 0.20612101698012242, 0.08392681575466732, + 0.09397681364766289, 0.061948998399705385, 0.1699040805655877}, + {0.4775145080229961, -0.20516430386037218, 0.07267587187915008, -0.026058009209961, 0.10395296641445628, + 0.005252605556895273, -0.02985365267173483, 0.05265019142871023}}, + {{0.5000201139246306, -0.04800345446956701, -0.0329049463626208, -0.03284470537729227, -0.20508598415359822, + -0.05090945428176026, 0.01485192433096992, -0.06668575548479515}, + {0.6977797423695709, -0.08329928338393976, -0.07437114892036442, 0.10800747144622731, + 0.18894316926384197, 0.11583460240335249, 0.017113084069833556, -0.27560766527667935}}, + {{0.4851180273990343, -0.16126949568571958, -0.06691674009092896, -0.10704718961757465, + -0.14611311380319633, -0.035002563984131076, 0.02014526618951898, -0.05833761506438867, + 0.18128233338238373, -0.03264166614264144, 0.12163209118053736, -0.09121827833544369, + -0.05000481625592715, 0.03207004406365072, -0.08147428234704425, 0.015384889697247134}, + {0.5677253092727153, -0.03729235153062347, -0.039365455693876845, 0.04146785092046644, + 0.09798188935435953, -0.008627080611540536, -0.06552852244983984, 0.10072436882505036, + -0.08081824922584976, -0.03604185729698669, 0.03292928365596532, -0.0122484248012707, + -0.04163904757760628, -0.02488167514502858, 0.10345023825137331, 0.039377537425709944}}, + {{0.3895162332979207, 0.04005874604287546}, {0.4678112991261936, -0.18348392546159248}}, + {{0.39940699287949466, 0.11176911860071312, 0.19911545861741672, 0.1902015206428416}, + {0.5939351124484545, 0.05900496787177262, 0.137922024588461, 0.0175179665929438}}, + {{0.3738855099605134, -0.061514135704676315, -0.2538398050680273, -0.00813228916077971}, + {0.7804515639944012, -0.0892065581024592, 0.002545891749693663, -0.07680650878619621}}, + {{0.5179707425469033, -0.025302819300953475, -0.08988468070186949, -0.15759688083449927, + 0.04986716326586882, 0.016711033222231685, 0.0893262152057811, 0.011837069383857, 0.08204319336636628, + -0.21049563858248832, 0.015427354145726171, -0.07133856817868453, -0.01279403797244072, + 0.10188626425620104, -0.18206676093811552, -0.07683334214910278}, + {0.4557183244222546, -0.10900814441739004, 0.04904230378999857, -0.0010906845210327046, + -0.031895227628587916, -0.037331861262286915, -0.040831099380204676, 0.0030723082051012374, + -0.10402962094045479, 0.09107053711775343, 0.14231804953997862, 0.06072556870526918, + 0.015183021067558186, -0.023570386570056008, -0.0692160119825995, -0.010341036241630921}}, + {{0.509266881164118, -0.23777343990417177, -0.16207618102797572, -0.037644746616759095}, + {0.4844952936065516, -0.13303389385421258, -0.25571531259653263, -0.05966291383459679}}, + {{0.5935176438976462, 0.005419535054208113, 0.05452812784476206, 0.05483345393049244, -0.04461918309489521, + 0.03860170688009229, 0.008260547169506763, 0.07814954434523974}, + {0.5579264002751396, -0.19510727214536616, -0.2776691321309364, 0.01833209516889496, + 0.043341981649842765, 0.09601908528205956, -0.003869164267408187, -0.16505054140412423}}, + {{0.6260210272262958, 0.02432692813809728, -0.003993313239643373, 0.12917970105240434, -0.07053644945003956, + 0.16506423245734803, -0.05346289230457359, 0.04336626138538329}, + {0.5241234456299799, -0.061299666157965914, 0.20381084080210454, -0.03215905928455756, + -0.05095139644888391, 0.03355389235031433, -0.04283903698729301, -0.3019386034408972}}, + {{0.5500660037041453, -0.0756423721419075, 0.2210689971105149, 0.07481755482487502, 0.10628452169723956, + -0.1243776248752409, -0.09116501409436648, 0.07374567803767317}, + {0.6070515456890231, -0.3012803549751432, 0.04193893532024, -0.08007158532442629, -0.006248541615777148, + -0.10324316179539066, -0.05306697572107942, 0.1322676210022765}}, + {{0.4651842021869838, 0.08699362121518653, -0.03136073598867131, 0.10751032697033988, 0.04427971547591952, + 0.019039286768015494, 0.18878104838596632, 0.1045724206875964}, + {0.39116023270347816, 0.031180108596409038, 0.32532105395892885, -0.11704207031957495, + 0.04658810674925612, 0.009016215044166007, -0.07362344496447837, -0.1848819833344538}}, + {{0.4885688910675007, -0.18086781613949704, -0.07671903790273893, 0.14878362006771667, 0.09590215124139359, + -0.07456619521904288, -0.03965753053553456, -0.08790199439607198}, + {0.474901408893455, -0.17244598635312056, -0.023956901826718974, 0.1791658005916335, + -0.036152996890597366, -0.012259503214089251, 0.07875989038950917, -0.12969705651831892}}, + {{0.5987680678550062, -0.11799613493734534, -0.2864743448783378, 0.19231233198393816}, + {0.36137018847278746, -0.019882285719344417, -0.04018093170042117, 0.010964307296384701}}, + {{0.5508858453605304, 0.051710056952484845, 0.004171556603573934, -0.14212685903305175}, + {0.5423592365640588, -0.10551677764322767, -0.1236491498948506, 0.02291325214037246}}, + {{0.4185139971798104, -0.12598968025560597}, {0.5135294651086221, -0.28462918232983503}}, + {{0.22832374343997397, 0.2228842887585636}, {0.4081714273612491, -0.11432324075082756}}, + {{0.7871915250462291, -0.1613647796008854}, {0.40649468585348525, -0.26414679771214}}, + {{0.6380761918458395, -0.022978944297255756, -0.0014396124781275166, -0.02384143615782447, + -0.07921880781102483, -0.06801029020723776, -0.15490186756072524, 0.03355342032660816, + 0.06685652476192966, -0.01586151971323415, -0.13389162215415135, 0.005533156811150963, + -0.060356774511329564, 0.17693167298435386, -0.03612458427971646, 0.11864376520170625}, + {0.6414668453372225, -0.012718518336902778, 0.024705495293850604, -0.07647416157394636, + 0.06296179906251137, 0.11361886055723146, -0.02420694444897769, -0.03841719639006607, + 0.05452753410368566, -0.01239440538295344, 0.032061027655300084, 0.06376752807408094, + 0.012832470978494881, -0.07360848144902933, -0.01965325840198135, -0.020673505041039646}}, + {{0.4039060952260403, 0.17105094720582825, -0.04687300825237843, 0.06312719676982903, -0.07152120584803218, + 0.12874155376912266, 0.2559406786392022, -0.1178795002459361}, + {0.4931432693943547, -0.04258744279908336, 0.07887713934060492, 0.012311293243742596, + 0.020586864135253208, 0.09532173093653501, 0.021804946261635733, 0.1467042712342011}}, + {{0.6492600132374694, 0.07405704598578203, -0.10652150413678427, 0.014145446840794215, -0.0763605684643836, + 0.07600883542245865, -0.03496573571710465, -0.06855537550667139, -0.04424787015115095, + 0.06760348676948996, -0.09178162857206457, -0.025320785111737663, 0.040561414773794824, + 0.13320415864997182, 0.021189849362183932, 0.042958204563214517}, + {0.40535288984911627, 0.17032068199517506, 0.01331219400387537, 0.03853473160782414, + -0.012526554240257781, -0.06187945408577552, -0.036479509907725347, -0.07188532577134454, + -0.0019327516265471578, -0.08139500313648848, -0.0058851768933687165, 0.025922987062677243, + 0.047024671064922474, 0.0211940396143128, -0.072339453496859, -0.06530692733234739}}, + {{0.07581743845849698, -0.03372147494491318}, {0.17613036197559018, -0.15384609099990643}}, + {{0.5928639944503687, 0.11132887192540089, 0.11133754396132797, 0.15144287134688658}, + {0.2912240472333921, -0.012080065326201012, 0.2276045060510091, 0.3304487787130108}}, + {{0.5374369429329483, 0.0124309756250367, -0.1263557788347015, 0.010780749436562376, 0.0469888925419365, + 0.15244935362834236, 0.0013926801264731864, -0.1150986978030912}, + {0.39114492523974376, 0.049146273279162184, 0.02579091351846495, -0.14222691912065577, + -0.027042182426221584, -0.14789058018667428, -0.03263438975452017, -0.0061769800609844105}}, + {{0.39574426536060486, 0.3848693124084244, 0.20872361615139384, -0.03259572209916245}, + {0.5654818630543237, -0.05016643956373015, 0.030751960689858693, 0.03337316792991071}}, + {{0.5281995925319434, -0.013483007703420306, 0.22370807173452606, 0.032600345010997386, -0.183706104923201, + 0.10078556921052059, -0.06643803387393597, -0.19216007772763355}, + {0.3839006686515898, 0.04203486886439816, -0.07423404752770826, -0.0167526930749399, + 0.03296043868103443, 0.07361362334006122, 0.06773560004500725, 0.1850404987428018}}, + {{0.940402493651157, 0.04333736944934541}, {0.7093290405641345, -0.25091026140046113}}, + {{0.7156356852777146, -0.15610326950991757, 0.189797885134394, 0.10323280721415865}, + {0.4766476514940784, 0.2619203051863527, -0.18127917611815178, -0.1767088675875917}}, + {{0.5632118136379611, 0.16086874310016572, 0.026195249820023803, 0.20506905118764587}, + {0.46178567640449736, -0.004178181340561221, -0.1319909533632702, 0.1519936998210166}}, + {{0.4016790839699139, -0.03003822458401474, -0.057149866644219526, -0.011524063301240554, + 0.034856641495165167, 0.13554174060998003, -0.09095916185362438, -0.0018328637444979812, + 0.005074060125136143, -0.03801226191198792, -0.043716698741379005, 0.0030306419969509946, + -0.024955698295149528, 0.007269331103218465, 0.042476489879821895, -0.10712547204424006}, + {0.5952054191724998, -0.07676059511106324, -0.026180524219684337, -0.04836907409057477, + 0.03599606184553125, -0.09752988130462281, -0.018060463584829087, 0.050791615691301736, + 0.034038777924507435, 0.07585998009242355, 0.04676163922757573, 0.16844664234113793, + -0.044975123197931816, -0.04892751473324741, -0.004238118680509565, 0.03828422498466859}}, + {{0.46901212184076757, -0.01535709462655225, -0.03916243436874867, 0.060857712029923704, 0.1225909913945583, + -0.09340926175888574, -0.1289223230381446, -0.010663880940625405, 0.00724348179779008, + -0.060944417834272645, 0.06976206691384879, 0.03620812577362522, -0.11481741841389, + -0.045575265138713114, -0.060986015410215574, 0.061727667678068}, + {0.5337093691722835, 0.04436500074224131, -0.030963373409186423, 0.020253982238142385, + -0.0824519992966499, 0.012010153722473894, 0.15829867840518805, 0.04533676610748462, + 0.1083306629496745, -0.08650086214010269, 0.045865730697915885, 0.026540593157353194, + 0.05755930064433876, 0.03279325594388378, 0.06317542465592338, -0.025317096700691388}}, + {{0.4122273004184882, -0.014566204465011313, 0.04208221121586804, 0.17869124295327457, + -0.038967419712763435, 0.03973015972596076, 0.005246463372848328, 0.0010137347804333452}, + {0.4077985676527511, 0.1686350835680268, -0.12037578482090629, 0.006478097285106572, + 0.07949583704816715, 0.032202813316253534, -0.016855152490183614, -0.0777645930586234}}, + {{0.45800711085320694, 0.2894221324360919}, {0.6497642460058267, 0.013090322012305977}}, + {{0.6031245930981106, -0.09986864407594251, 0.11555260315746024, 0.035732701010434215}, + {0.3289125819706231, 0.2696416277863902, -0.13227435752865208, -0.14598036756331914}}, + {{0.6114313386227679, -0.002426979989567729, 0.2651802480809971, 0.028670273025861864}, + {0.651170538927121, -0.04306325057145169, -0.03318614112600926, -0.3055036135167195}}, + {{0.4957183696678124, -0.10553486119506748, 0.052758247615336755, 0.06020035276185396, + -0.013822765236362097, -0.02389059354934929, -0.08857441873167826, 0.008621444261494117, + 0.02082676340882711, 0.05884859157016205, -0.04918841735798534, 0.1405177719428041, 0.0681786035653371, + 0.10947244481774009, 0.08033111333375087, -0.04045006452645019}, + {0.6574845762838595, -0.031453441409394496, -0.04620430117138156, -0.08424489796710648, + 0.06624180213432235, -0.13120364199595552, -0.024391758054135494, 0.08573068590472949, + 0.11223336151893865, 0.07527943774180709, -0.0024984661992818805, 0.09946105848481208, + 0.06606087488780027, -0.022974547547147776, 0.12387018801680866, 0.02304817543478708}}, + {{0.8882569099677424, -0.059651058771480525}, {0.7868319065484695, -0.03956869175289757}}, + {{0.45166687758041546, -0.0018649641234558647, 0.04104745825547753, -0.04331775004206736, + 0.04599538731492686, -0.03347890915900812, 0.05706572867550946, 0.04407075696339463, + -0.09550030045209212, -0.01294296290526355, -0.028171062627338407, -0.011479435442369852, + -0.126578568096198, -0.1055877835747785, -0.01335447270374341, 0.0261870443942967}, + {0.5375290203239034, -0.02717126518144706, 0.03769657408945279, 0.05903704597317041, + 0.10941228619740563, 0.034836047977018406, 0.11449317534415826, 0.03329000205035401, + -0.18630596238167652, -0.03847833560601357, 0.11119698020303745, 0.10426826809732498, + -0.10133153669488919, 0.06085072384786873, -0.08524817187204464, 0.016337527326520968}}, + {{0.5672530754492042, -0.07621450260422805, 0.04780501932723003, 0.1360648011834046, -0.13636811964603351, + -0.05559164579848517, -0.07319574079493588, -0.022375181723106995, -0.01803709042623941, + 0.012598739926626794, 0.003834329770772228, 0.02279304289413908, -0.06706984366225677, + -0.023519044517253875, 0.05957470708250281, -0.041378144644557704}, + {0.6927367377783716, -0.08658190014174541, -0.044710161680140095, -0.10627851813733222, + -0.08408056627365093, 0.02806037261656114, 0.03681651148127257, -0.0438823647392234, + -0.08259784729478026, -0.0660019535766834, -0.06364239429924656, 0.005461792317948917, + -0.04090024064986589, 0.022673859550358066, -0.0015713160185880162, -0.021211377685855435}}, + {{0.5615331442331852, 0.09973756929788653, -0.043561441091611794, 0.10577901659492642, 0.027355604592400162, + -0.07846639287809637, 0.06769255556633608, 0.11495862363660969, 0.08994548302237715, + -0.029898108867756554, 0.07176626712854703, -0.04707555302652763, 0.016817577757129054, + -0.14296761759104473, 0.03811568418894253, 0.0662515936881235}, + {0.37970420079142386, 0.035427020131707, 0.03758585103249978, 0.09252346890432356, 0.08421133424276199, + 0.0013340546210409143, 0.021541970955435447, 0.03079966148676702, 0.022053247502089618, + -0.05621550573850214, -0.07144813696100247, 0.0005035402070686645, -0.04253374666480074, + 0.16004586961594686, -0.01426637100829721, -0.1376968509881538}}}; - for (int i = 0; i < inputData.length; i++) { - double[] re = inputData[i]; // Real part of input - double[] im = new double[re.length]; // Imaginary part of input + for(int i = 0; i < inputData.length; i++) { + double[] re = inputData[i]; // Real part of input + double[] im = new double[re.length]; // Imaginary part of input - double[][] expected = outputData[i]; // Expected output + double[][] expected = outputData[i]; // Expected output - ifft(re, im, 1, re.length); // Perform IFFT + ifft(re, im, 1, re.length); // Perform IFFT - double[][] actual = new double[][] { re, im }; - // Validate the IFFT results - validateComplexIFftResults(expected, actual, i + 1); - } - } + double[][] actual = new double[][] {re, im}; + // Validate the IFFT results + validateComplexIFftResults(expected, actual, i + 1); + } + } - private void validateComplexIFftResults(double[][] expected, double[][] actual, int lineNumber) { + private void validateComplexIFftResults(double[][] expected, double[][] actual, int lineNumber) { - int length = expected[0].length; + int length = expected[0].length; - for (int i = 0; i < length; i++) { - double realActual = actual[0][i]; - double imagActual = actual[1][i]; - assertEquals("Mismatch in real part at index " + i + " in line " + lineNumber, expected[0][i], realActual, - 1e-9); - assertEquals("Mismatch in imaginary part at index " + i + " in line " + lineNumber, expected[1][i], - imagActual, 1e-9); - } + for(int i = 0; i < length; i++) { + double realActual = actual[0][i]; + double imagActual = actual[1][i]; + assertEquals("Mismatch in real part at index " + i + " in line " + lineNumber, expected[0][i], realActual, + 1e-9); + assertEquals("Mismatch in imaginary part at index " + i + " in line " + lineNumber, expected[1][i], + imagActual, 1e-9); + } - if (lineNumber % progressInterval == 0) { - System.out.println("ifft(complex input): Finished processing line " + lineNumber); - } + if(lineNumber % progressInterval == 0) { + System.out.println("ifft(complex input): Finished processing line " + lineNumber); + } - } + } - // Helper method for asserting equality with a tolerance - private static void assertEquals(String message, double expected, double actual, double tolerance) { - assertTrue(message + " - Expected: " + expected + ", Actual: " + actual, - Math.abs(expected - actual) <= tolerance); - } + // Helper method for asserting equality with a tolerance + private static void assertEquals(String message, double expected, double actual, double tolerance) { + assertTrue(message + " - Expected: " + expected + ", Actual: " + actual, + Math.abs(expected - actual) <= tolerance); + } - private void assertComplexEquals(String message, double expectedReal, double expectedImag, double actualReal, - double actualImag) { - final double EPSILON = 1e-9; - assertTrue(message + " - Mismatch in real part. Expected: " + expectedReal + ", Actual: " + actualReal, - Math.abs(expectedReal - actualReal) <= EPSILON); - assertTrue(message + " - Mismatch in imaginary part. Expected: " + expectedImag + ", Actual: " + actualImag, - Math.abs(expectedImag - actualImag) <= EPSILON); - } + private void assertComplexEquals(String message, double expectedReal, double expectedImag, double actualReal, + double actualImag) { + final double EPSILON = 1e-9; + assertTrue(message + " - Mismatch in real part. Expected: " + expectedReal + ", Actual: " + actualReal, + Math.abs(expectedReal - actualReal) <= EPSILON); + assertTrue(message + " - Mismatch in imaginary part. Expected: " + expectedImag + ", Actual: " + actualImag, + Math.abs(expectedImag - actualImag) <= EPSILON); + } - double[][] reInputs = { { 0.6749989259154331, 0.6278845555308362, 0.995916990652601, 0.511472971081564 }, - { 0.5131170807843421, 0.21947931088584838, 0.8291802449178103, 0.7771605380836767, 0.8702643857540487, - 0.8710395079045988, 0.38542176916377324, 0.2881994247798211, 0.1928317731008281, - 0.26952861717454657, 0.15144147822174414, 0.41960334787295883, 0.45034112558576034, - 0.1583971541867757, 0.957780647411057, 0.6758004971959929 }, - { 0.307168369495708, 0.2925436063741176, 0.41391507728373833, 0.3005219437466511, 0.5878469029586312, - 0.12931493281234274, 0.9278465629379351, 0.17121813246774298, 0.4177834301993081, - 0.3769141990814494, 0.6753379435396163, 0.38247139181867484, 0.24735225836867192, - 0.31494194715109947, 0.8137890716670311, 0.20924977998686056 }, - { 0.12235304422259108, 0.8698130261511119, 0.4337042152439805, 0.6857925372785122, 0.14428854345470843, - 0.33600839167062124, 0.8119488842140201, 0.11798945578653453, 0.010947620425100557, - 0.9717416040360395, 0.12125654222557414, 0.2617819654240723, 0.6486301555911228, 0.7365333531211434, - 0.2853381645844496, 0.245649529407872, 0.6297339098238705, 0.27490823498920813, 0.16976743283098572, - 0.6735736955293987, 0.6966873975788616, 0.03317205897839892, 0.06774514015627431, - 0.2345911011238595, 0.056861430818630154, 0.9458602964130863, 0.809611874188032, 0.2508944819690482, - 0.41904987235844393, 0.44527473435246423, 0.3598767804057533, 0.6738051864030766, - 0.04313688787812686, 0.14040411518698392, 0.6279634929162148, 0.6907852049110121, - 0.010900703530978717, 0.22548934759676642, 0.42249315955719224, 0.7566767789254828, - 0.5549511666098619, 0.2132782383128563, 0.13319453700750528, 0.9913307843402898, 0.5918930102432697, - 0.8123119094264839, 0.06669536255452357, 0.1404352024672152, 0.5822461498734745, 0.9507715524385827, - 0.5149268016834162, 0.46487304063142065, 0.14177878291874513, 0.3030540409436946, - 0.7390807099554567, 0.07855298022742563, 0.7267851554463288, 0.3003808861768813, 0.6942310295833771, - 0.02659985118413699, 0.08047625367936184, 0.25129044839422787, 0.21921500696568008, - 0.6446997331877551 }, - { 0.6467394714302301, 0.37939437912607255, 0.24389382952413974, 0.9230517610649195 }, - { 0.8133403772356219, 0.9685127164754849, 0.9527118851443901, 0.16168777807158385, 0.3059554069336482, - 0.5098875351782225, 0.18776670485231783, 0.9552937947531795, 0.26630009198521765, - 0.7607275302116739, 0.22743564839228736, 0.2579812237254093, 0.16461081160421187, - 0.2568798739085394, 0.6664580672719578, 0.611154187704012 }, - { 0.27346385127769657, 0.9768706836769414, 0.63435141212605, 0.09267578126266052, 0.6091291796709725, - 0.8003601253351955, 0.21441231066064415, 0.7574882595853318, 0.6175964091894415, - 0.026447112091714353, 0.9838155477830359, 0.2879060542830987, 0.293010252419861, - 0.44472456980740893, 0.18371859899106335, 0.9697152014778653 }, - { 0.9241946993360354, 0.93609012256163, 0.7262744168234273, 0.8953331805633857, 0.6737803589116659, - 0.12819148236368838, 0.18661306972239466, 0.7389050320153769, 0.7671764150606258, - 0.4513549998697196, 0.8730680721562523, 0.5506214841129061, 0.03430880982544926, 0.7895272245140907, - 0.9546984540561432, 0.42833058697767035, 0.2900777210978328, 0.8562805913655864, 0.5473173303230449, - 0.4148313135430701, 0.24482377481230033, 0.1537057907732975, 0.11035564325990421, - 0.5386796809145485, 0.8661412254170333, 0.8498517303709959, 0.5906863904920534, 0.9078942281308974, - 0.7387794781959018, 0.720441512828483, 0.9561370849174334, 0.3347594847372559, 0.48786193718206394, - 0.9919064656347301, 0.4058367062974416, 0.8130018855584936, 0.7671992644775938, 0.7621070450721142, - 0.2968734799496455, 0.08903123300124016, 0.8438017683491356, 0.9911353232857506, 0.7124852788354706, - 0.8772702766537811, 0.34692831949403335, 0.6506408096815364, 0.22319555392893964, - 0.33156033109536054, 0.8234621132297556, 0.9015756069246127, 0.4512357762048391, - 0.12584571471970663, 0.53112509650375, 0.3972399589722395, 0.5149797348786508, 0.7327411500784157, - 0.45127893858601775, 0.016770807338283178, 0.26719900538319863, 0.3129575148836051, - 0.8896414390923291, 0.40604886799160245, 0.11009325815063131, 0.2697706263683922 }, - { 0.8287813507102393, 0.1908501784393708, 0.2003356406350958, 0.6681197614203025 }, - { 0.06238193735772257, 0.27323433977164435, 0.040380413052192865, 0.19662508742378038 } }; + double[][] reInputs = {{0.6749989259154331, 0.6278845555308362, 0.995916990652601, 0.511472971081564}, + {0.5131170807843421, 0.21947931088584838, 0.8291802449178103, 0.7771605380836767, 0.8702643857540487, + 0.8710395079045988, 0.38542176916377324, 0.2881994247798211, 0.1928317731008281, 0.26952861717454657, + 0.15144147822174414, 0.41960334787295883, 0.45034112558576034, 0.1583971541867757, 0.957780647411057, + 0.6758004971959929}, + {0.307168369495708, 0.2925436063741176, 0.41391507728373833, 0.3005219437466511, 0.5878469029586312, + 0.12931493281234274, 0.9278465629379351, 0.17121813246774298, 0.4177834301993081, 0.3769141990814494, + 0.6753379435396163, 0.38247139181867484, 0.24735225836867192, 0.31494194715109947, 0.8137890716670311, + 0.20924977998686056}, + {0.12235304422259108, 0.8698130261511119, 0.4337042152439805, 0.6857925372785122, 0.14428854345470843, + 0.33600839167062124, 0.8119488842140201, 0.11798945578653453, 0.010947620425100557, 0.9717416040360395, + 0.12125654222557414, 0.2617819654240723, 0.6486301555911228, 0.7365333531211434, 0.2853381645844496, + 0.245649529407872, 0.6297339098238705, 0.27490823498920813, 0.16976743283098572, 0.6735736955293987, + 0.6966873975788616, 0.03317205897839892, 0.06774514015627431, 0.2345911011238595, 0.056861430818630154, + 0.9458602964130863, 0.809611874188032, 0.2508944819690482, 0.41904987235844393, 0.44527473435246423, + 0.3598767804057533, 0.6738051864030766, 0.04313688787812686, 0.14040411518698392, 0.6279634929162148, + 0.6907852049110121, 0.010900703530978717, 0.22548934759676642, 0.42249315955719224, 0.7566767789254828, + 0.5549511666098619, 0.2132782383128563, 0.13319453700750528, 0.9913307843402898, 0.5918930102432697, + 0.8123119094264839, 0.06669536255452357, 0.1404352024672152, 0.5822461498734745, 0.9507715524385827, + 0.5149268016834162, 0.46487304063142065, 0.14177878291874513, 0.3030540409436946, 0.7390807099554567, + 0.07855298022742563, 0.7267851554463288, 0.3003808861768813, 0.6942310295833771, 0.02659985118413699, + 0.08047625367936184, 0.25129044839422787, 0.21921500696568008, 0.6446997331877551}, + {0.6467394714302301, 0.37939437912607255, 0.24389382952413974, 0.9230517610649195}, + {0.8133403772356219, 0.9685127164754849, 0.9527118851443901, 0.16168777807158385, 0.3059554069336482, + 0.5098875351782225, 0.18776670485231783, 0.9552937947531795, 0.26630009198521765, 0.7607275302116739, + 0.22743564839228736, 0.2579812237254093, 0.16461081160421187, 0.2568798739085394, 0.6664580672719578, + 0.611154187704012}, + {0.27346385127769657, 0.9768706836769414, 0.63435141212605, 0.09267578126266052, 0.6091291796709725, + 0.8003601253351955, 0.21441231066064415, 0.7574882595853318, 0.6175964091894415, 0.026447112091714353, + 0.9838155477830359, 0.2879060542830987, 0.293010252419861, 0.44472456980740893, 0.18371859899106335, + 0.9697152014778653}, + {0.9241946993360354, 0.93609012256163, 0.7262744168234273, 0.8953331805633857, 0.6737803589116659, + 0.12819148236368838, 0.18661306972239466, 0.7389050320153769, 0.7671764150606258, 0.4513549998697196, + 0.8730680721562523, 0.5506214841129061, 0.03430880982544926, 0.7895272245140907, 0.9546984540561432, + 0.42833058697767035, 0.2900777210978328, 0.8562805913655864, 0.5473173303230449, 0.4148313135430701, + 0.24482377481230033, 0.1537057907732975, 0.11035564325990421, 0.5386796809145485, 0.8661412254170333, + 0.8498517303709959, 0.5906863904920534, 0.9078942281308974, 0.7387794781959018, 0.720441512828483, + 0.9561370849174334, 0.3347594847372559, 0.48786193718206394, 0.9919064656347301, 0.4058367062974416, + 0.8130018855584936, 0.7671992644775938, 0.7621070450721142, 0.2968734799496455, 0.08903123300124016, + 0.8438017683491356, 0.9911353232857506, 0.7124852788354706, 0.8772702766537811, 0.34692831949403335, + 0.6506408096815364, 0.22319555392893964, 0.33156033109536054, 0.8234621132297556, 0.9015756069246127, + 0.4512357762048391, 0.12584571471970663, 0.53112509650375, 0.3972399589722395, 0.5149797348786508, + 0.7327411500784157, 0.45127893858601775, 0.016770807338283178, 0.26719900538319863, 0.3129575148836051, + 0.8896414390923291, 0.40604886799160245, 0.11009325815063131, 0.2697706263683922}, + {0.8287813507102393, 0.1908501784393708, 0.2003356406350958, 0.6681197614203025}, + {0.06238193735772257, 0.27323433977164435, 0.040380413052192865, 0.19662508742378038}}; - double[][] imInputs = { { 0.8330832079105173, 0.09857986129294982, 0.6808883894146879, 0.28353782431047303 }, - { 0.11858471405750115, 0.6670449264234233, 0.7918053251189741, 0.18474324421305366, 0.2267737011892611, - 0.6112631028641866, 0.43732697326158654, 0.024562867771134922, 0.5846651001654705, - 0.9231757238526722, 0.32785888537720953, 0.058059707791723425, 0.888849882806027, - 0.34043171288025054, 0.37505905249694726, 0.4989225900943882 }, - { 0.19515621126922889, 0.9663577672470134, 0.052248997892968774, 0.3368420822314041, 0.25591814564245663, - 0.31140858941730654, 0.6214194316782065, 0.898776506616156, 0.21908542333722314, 0.1721154555352078, - 0.8129941790492901, 0.11019299590688614, 0.9358894676906747, 0.5566948949725117, 0.5712632308813113, - 0.6198873277092072 }, - { 0.7553443433772105, 0.7149725143780661, 0.26342945108194404, 0.6073384956030843, 0.6682926680358615, - 0.9391571286734728, 0.6857282535520356, 0.8074713507427728, 0.9936095082352023, 0.7050591882477736, - 0.8307563345962672, 0.4504495350884602, 0.6669656135676392, 0.8229995389638302, 0.9724224050712397, - 0.4465681389737993, 0.830115912596753, 0.5758021742895939, 0.2555926327785295, 0.7229147142946879, - 0.19465587302731124, 0.6455358466934192, 0.9895816229678752, 0.27617594094673303, - 0.27676552480841954, 0.950721135261892, 0.06159792234410011, 0.25388932381509377, - 0.027723112627898172, 0.6255532725359773, 0.8145203264634711, 0.8254031856352116, - 0.25282878966017297, 0.5652351235601151, 0.43847171025625065, 0.2970657957526097, - 0.03860912616964929, 0.9667833637002887, 0.8313877744010215, 0.8064932753796599, - 0.04382793626063919, 0.23236021244230354, 0.9946493719154478, 0.9442462780462477, - 0.34190146836022484, 0.4801148300313609, 0.4347269847824623, 0.1934175418869568, 0.9493835173004894, - 0.008847061440726112, 0.43992481101923, 0.9975703488985399, 0.20833318425512648, - 0.39400365256608416, 0.43378030144507285, 0.2626310071775566, 0.35102954497285954, - 0.9000857989597337, 0.960793699725036, 0.4818308285625894, 0.258367005138464, 0.7955496343862104, - 0.38227049297061555, 0.5309262232624709 }, - { 0.030027811041104635, 0.24756223021053136, 0.6932959203102655, 0.31779244778670823 }, - { 0.057812427817348744, 0.48563678221308515, 0.6385926917596327, 0.06866424557822048, 0.7286057309387103, - 0.578198895275423, 0.35806649041869165, 0.17544934833578907, 0.9546365831179597, 0.7608272984745191, - 0.18047691393328225, 0.6719741912319742, 0.3254545042940935, 0.5391892808596687, 0.4610969186702265, - 0.6058329787299822 }, - { 0.5731193504743051, 0.8285838565973793, 0.033091935005607365, 0.9929008886851187, 0.2702292788833681, - 0.17372141606262959, 0.24616279825199905, 0.759464525093345, 0.37795458115492475, - 0.7612050359964757, 0.3364320358467179, 0.8518930134503825, 0.639680370851991, 0.5647886652079595, - 0.06383168132690153, 0.941871175081797 }, - { 0.10654574064225608, 0.18112904630201787, 0.7817037292161019, 0.748279488764987, 0.7485017168905912, - 0.4270901026653211, 0.5037981228936685, 0.8413518361670483, 0.8918350666826977, 0.8960201986678724, - 0.04380182167274471, 0.36899360872947595, 0.4792421777277245, 0.09414311412615073, - 0.9810071872876617, 0.9701739706435585, 0.566192946273844, 0.4761038133937181, 0.07930270657906191, - 0.6958808238326885, 0.07288965713564122, 0.32843359695282626, 0.27790842403879157, - 0.6387041119338002, 0.15788528537840396, 0.2667380867035477, 0.7715682128215527, - 0.09996918315097514, 0.2690373433561819, 0.1530087585637756, 0.3879254571666868, 0.3251278338384528, - 0.2663572874336204, 0.5826546948845359, 0.8831125706700407, 0.5502460958047991, 0.4710428569771352, - 0.7530740137353021, 0.9754266619245848, 0.377421578189048, 0.01652242589646613, 0.8244149593142929, - 0.6820110251541992, 0.4002296116241395, 0.2955913658044953, 0.7362095700182021, 0.40770234279226714, - 0.87172045299516, 0.039230936916690995, 0.02750586292612267, 0.10078915278495215, - 0.8983285228828739, 0.32618356004699833, 0.6886593271816672, 0.07577625227317097, - 0.3447942876325266, 0.39914263324350274, 0.827412575862219, 0.34354508675330786, - 0.09570201879504958, 0.3370302990354914, 0.8158265013181055, 0.6293777092101274, - 0.431976260298389 }, - { 0.09433683850154906, 0.6852026530097579, 0.32427852025540604, 0.25548685811214333 }, - { 0.04917788114994759, 0.3254526123103906, 0.007583733861966979, 0.7225054664397857 } }; + double[][] imInputs = {{0.8330832079105173, 0.09857986129294982, 0.6808883894146879, 0.28353782431047303}, + {0.11858471405750115, 0.6670449264234233, 0.7918053251189741, 0.18474324421305366, 0.2267737011892611, + 0.6112631028641866, 0.43732697326158654, 0.024562867771134922, 0.5846651001654705, 0.9231757238526722, + 0.32785888537720953, 0.058059707791723425, 0.888849882806027, 0.34043171288025054, 0.37505905249694726, + 0.4989225900943882}, + {0.19515621126922889, 0.9663577672470134, 0.052248997892968774, 0.3368420822314041, 0.25591814564245663, + 0.31140858941730654, 0.6214194316782065, 0.898776506616156, 0.21908542333722314, 0.1721154555352078, + 0.8129941790492901, 0.11019299590688614, 0.9358894676906747, 0.5566948949725117, 0.5712632308813113, + 0.6198873277092072}, + {0.7553443433772105, 0.7149725143780661, 0.26342945108194404, 0.6073384956030843, 0.6682926680358615, + 0.9391571286734728, 0.6857282535520356, 0.8074713507427728, 0.9936095082352023, 0.7050591882477736, + 0.8307563345962672, 0.4504495350884602, 0.6669656135676392, 0.8229995389638302, 0.9724224050712397, + 0.4465681389737993, 0.830115912596753, 0.5758021742895939, 0.2555926327785295, 0.7229147142946879, + 0.19465587302731124, 0.6455358466934192, 0.9895816229678752, 0.27617594094673303, 0.27676552480841954, + 0.950721135261892, 0.06159792234410011, 0.25388932381509377, 0.027723112627898172, 0.6255532725359773, + 0.8145203264634711, 0.8254031856352116, 0.25282878966017297, 0.5652351235601151, 0.43847171025625065, + 0.2970657957526097, 0.03860912616964929, 0.9667833637002887, 0.8313877744010215, 0.8064932753796599, + 0.04382793626063919, 0.23236021244230354, 0.9946493719154478, 0.9442462780462477, 0.34190146836022484, + 0.4801148300313609, 0.4347269847824623, 0.1934175418869568, 0.9493835173004894, 0.008847061440726112, + 0.43992481101923, 0.9975703488985399, 0.20833318425512648, 0.39400365256608416, 0.43378030144507285, + 0.2626310071775566, 0.35102954497285954, 0.9000857989597337, 0.960793699725036, 0.4818308285625894, + 0.258367005138464, 0.7955496343862104, 0.38227049297061555, 0.5309262232624709}, + {0.030027811041104635, 0.24756223021053136, 0.6932959203102655, 0.31779244778670823}, + {0.057812427817348744, 0.48563678221308515, 0.6385926917596327, 0.06866424557822048, 0.7286057309387103, + 0.578198895275423, 0.35806649041869165, 0.17544934833578907, 0.9546365831179597, 0.7608272984745191, + 0.18047691393328225, 0.6719741912319742, 0.3254545042940935, 0.5391892808596687, 0.4610969186702265, + 0.6058329787299822}, + {0.5731193504743051, 0.8285838565973793, 0.033091935005607365, 0.9929008886851187, 0.2702292788833681, + 0.17372141606262959, 0.24616279825199905, 0.759464525093345, 0.37795458115492475, 0.7612050359964757, + 0.3364320358467179, 0.8518930134503825, 0.639680370851991, 0.5647886652079595, 0.06383168132690153, + 0.941871175081797}, + {0.10654574064225608, 0.18112904630201787, 0.7817037292161019, 0.748279488764987, 0.7485017168905912, + 0.4270901026653211, 0.5037981228936685, 0.8413518361670483, 0.8918350666826977, 0.8960201986678724, + 0.04380182167274471, 0.36899360872947595, 0.4792421777277245, 0.09414311412615073, 0.9810071872876617, + 0.9701739706435585, 0.566192946273844, 0.4761038133937181, 0.07930270657906191, 0.6958808238326885, + 0.07288965713564122, 0.32843359695282626, 0.27790842403879157, 0.6387041119338002, 0.15788528537840396, + 0.2667380867035477, 0.7715682128215527, 0.09996918315097514, 0.2690373433561819, 0.1530087585637756, + 0.3879254571666868, 0.3251278338384528, 0.2663572874336204, 0.5826546948845359, 0.8831125706700407, + 0.5502460958047991, 0.4710428569771352, 0.7530740137353021, 0.9754266619245848, 0.377421578189048, + 0.01652242589646613, 0.8244149593142929, 0.6820110251541992, 0.4002296116241395, 0.2955913658044953, + 0.7362095700182021, 0.40770234279226714, 0.87172045299516, 0.039230936916690995, 0.02750586292612267, + 0.10078915278495215, 0.8983285228828739, 0.32618356004699833, 0.6886593271816672, 0.07577625227317097, + 0.3447942876325266, 0.39914263324350274, 0.827412575862219, 0.34354508675330786, 0.09570201879504958, + 0.3370302990354914, 0.8158265013181055, 0.6293777092101274, 0.431976260298389}, + {0.09433683850154906, 0.6852026530097579, 0.32427852025540604, 0.25548685811214333}, + {0.04917788114994759, 0.3254526123103906, 0.007583733861966979, 0.7225054664397857}}; - double[][][][] expectedOutputs = { { - { - { 2.810273443180434, 0.5315583899556339 }, - { -0.20450648028789575, -0.43732964918644013 }, - }, - { - { 1.896089282928628, 1.1318539117217823 }, - { -0.03276314452169382, 0.3371527815133526 }, - }, - }, - { - { - { 8.029586903023583, 1.4783572816608272, 0.6711701068551449, -2.0728968306396376 }, - { 0.5021953651101563, -2.5648553213785314, 0.29423626194475117, 0.4012601985906169 }, - { -1.2849021209400728, 0.6671323763730415, -0.6695725808397565, -1.1712843044117673 }, - { 2.108868551493044, 1.0843197356522687, 1.0867961189703699, -0.35053844891456276 }, - }, - { - { 7.05912751036381, 0.5291423797442222, 0.4427197585821442, -0.755496055817138 }, - { -0.30418687059677985, -2.2598935800730793, 0.6047902140223604, -1.5847243484575326 }, - { 0.25274774363624575, 0.05354074350183502, -0.46293891370557894, -1.4928446525217676 }, - { 0.04102445584853265, 1.2150529213724437, -0.3501635847389326, -1.0905422962407672 }, - }, - }, - { - { - { 6.568215549889579, -1.2298598996976158, 2.2138636830117013, -1.3116154891143874 }, - { -1.1345702156384125, 0.1358688749140914, -0.869135880783189, -1.2945083094997623 }, - { -0.23490362681105115, 1.8841337467288004, -1.290356324016746, -0.7998632424301513 }, - { 0.057854280160745564, 1.3009331869650396, 0.4577001084229444, 0.4609374698297427 }, - }, - { - { 7.6362507070770524, -0.5021300289612729, -0.30830053219433307, -0.40162315416311345 }, - { 0.00532353080901915, 1.0369754448667339, -2.483776588979377, -0.016217813328190278 }, - { -1.9062644821386074, -0.3728019954908228, -0.3037464465492685, -0.527450990728018 }, - { 0.46711047881499734, 0.4414987825805363, -1.1273549935419012, 1.4850074622342282 }, - }, - }, - { - { - { 26.68609298551157, -1.3338953635792201, 0.4020594424454147, 0.7401299103272476, - -2.810554548459751, -1.2778878008097723, -2.434717541683333, 1.8448958370317132 }, - { 1.5124772568141867, 2.997435519224954, 0.850391793491705, -1.409268535773998, - 0.7298732550934647, -1.0941883198448044, -0.4717054661204718, 2.2295601902248148 }, - { 0.9413326138415741, -3.5206438979732253, -1.860288065902871, -2.154762617920012, - 1.4998965860283968, 4.037874687522323, -2.97033202289703, -1.0678593057556733 }, - { 1.6739716993045417, -0.05711258741013203, -2.3676553799709974, 0.5456868972025865, - 0.1948281831859553, 0.7526107332947336, 5.065447419515738, 0.020456210203669833 }, - { -0.6956713490957522, -1.8296524399484828, -3.1697164039855377, 2.237563147861894, - 1.4551539353995173, -0.19554352732365277, -0.4636431658812785, 2.8849067509584243 }, - { 1.2874212252086117, -2.007455023954, 0.41828409822144785, 2.1560540097992273, - -0.9444730547029133, -1.9570758407351865, -2.7449964653128944, - -2.1931578153825004 }, - { -1.1727630961580466, -0.4626944940820328, -0.6026604126017501, -1.147197715423295, - -4.969175678925014, -1.8568706158045902, -4.704146412253633, 3.2664484067588457 }, - { -2.0576765512500503, 0.06751753454362419, 0.412051207702436, 0.026723509175442417, - 0.8659815323685047, 0.6298326096441218, -1.0225568132581624, -1.5743660274808011 }, - }, - { - { 35.87455968396184, -1.5248270601889629, -3.804141676368115, 1.8347304094358663, - -2.5797852364328016, 4.556415677676634, -2.059620257585239, 3.3259090771947486 }, - { 3.8330193771856442, 3.96923392685639, -2.4257908732841234, -0.5143874873871535, - 2.544553958429333, 5.542586595696317, 4.951043571220989, -1.6001504185486546 }, - { 1.572704438375391, 2.68337930664312, -0.8033985360422281, -2.6558624309276784, - -0.40085422767947554, -4.000393284174368, -4.331102876667192, 3.506899629433532 }, - { -2.4680885926531975, -0.8695810917508298, 2.9456617602301707, -0.24732914730729616, - 0.3430542822308015, 3.485849838657556, 3.9649335464206823, -1.680341105583727 }, - { -0.22764415191794996, -0.4951316522431507, 4.061903443125903, -0.6424984523403212, - -0.12529040791295087, 5.354293685375573, 0.040525918509912495, 1.0133620066630307 }, - { 0.6469092912664327, -1.9089096532567051, -3.0326019092676626, -3.6447657932795416, - -2.122099108477178, -0.7865053946835596, -0.1566125392915363, 1.984921157087272 }, - { 1.3348166868775824, -1.2189282274599857, 1.2479841062752444, -0.948271682640534, - -3.975769852998465, -1.562443050085742, 1.6163117823648547, -4.4062925310517995 }, - { 2.9675969104598394, -3.280556379469013, 2.3899027415100598, -1.6067363579675273, - 0.7470324060379839, -2.499741728611057, 2.986670707313758, 1.6217252291607327 }, - }, - }, - { - { - { 2.193079441145362, -0.41181283923662215 }, - { -0.14081174003275643, 0.9465030238449373 }, - }, - { - { 1.2886784093486097, 0.15796905335413058 }, - { -0.7334983268453378, -0.593037891692984 }, - }, - }, - { - { - { 8.066703633447759, 0.35776587504447654, -0.8975456466084525, -1.3260971108489834 }, - { 1.2925550450271355, 0.7471970785662574, 1.8523195957640421, -0.09130567177735105 }, - { 0.75069086903558, 0.45287128407866684, 1.1193031551551829, 0.11343169446248846 }, - { 1.4750614801978492, -0.4474301227850148, 0.46932996702100105, -0.9214050900106854 }, - }, - { - { 7.590515281648607, -0.08161444013345664, -0.18103075974871663, 0.9381669029060151 }, - { -1.5770093406180952, -2.7484477676036216, 1.3742861198499816, -1.2015040141484536 }, - { 0.046726986603437215, -2.150769239162087, -0.1301370419904344, 2.067734397359103 }, - { -1.057408338160801, -0.5695893624855741, -0.4947019509681271, -0.9002185882701961 }, - }, - }, - { - { - { 8.16568534963898, -1.440928805449021, -0.5466902254014514, 0.9947324514433773 }, - { -0.6989972691812494, -1.187942183202632, -1.062436068043263, 0.09504092090567329 }, - { -0.3794316462577032, -0.523294612518167, 2.7973454035250693, -1.9389358312439806 }, - { 0.8221904791733652, 1.051347229425449, -1.835143916223776, 0.0628803438524741 }, - }, - { - { 8.414930607970902, 1.0408479366310592, -3.3339265443792727, 1.3220823252356668 }, - { -0.390009888242036, -0.7239135908698271, -0.5199660526428092, 1.1500729000277474 }, - { 1.0954307864509207, -1.1232199355230432, -0.8940432401163299, 1.086489516763935 }, - { 0.590432616869855, -0.5703843580205217, -0.11315800207193116, 2.138244529504565 }, - }, - }, - { - { - { 35.97203267785346, 0.45863783607284137, 1.173056641524758, 0.7826325498969262, - -0.7567714479514733, 0.8900030139481167, 2.3340075668593485, 2.7783597078640225 }, - { -1.8297969735165558, 2.273178623633307, -0.48372099384482214, -0.40852587671049156, - 3.760728469728649, -0.7610308335136011, 1.2919699452742253, 4.670040243629213 }, - { 4.8372690355107135, -1.3261208568775944, 1.46633732066654, -0.0992228827598397, - -1.9223527786243282, 0.44458038637838815, 1.5533185398931715, 0.24496224499509434 }, - { 1.6661974146730905, 3.8314516505325447, -1.589204977232685, -2.9696832529162926, - 1.079869594848176, 2.8153541217824465, 3.0882863865289134, -2.28027259208448 }, - { -1.0570779237084942, 2.806843738194792, -1.4276915996739796, -3.4905691035061417, - -2.230138814152109, -2.872817891174476, 3.932782623656472, 1.1162539578269415 }, - { 4.289222018755856, 2.105500347856227, -1.0762009261508743, -1.6228254057953164, - -2.190533559398732, 1.1004383893378056, 1.1709877234229678, 3.1232851514701316 }, - { -0.45942227177196493, -1.819905457869288, 1.4446474205856596, 0.5520635899236316, - 1.3655329844463444, 2.67212745690105, -0.57694981659637, -3.6005960797530303 }, - { -1.743365079415264, -2.335615239269563, -1.8778197199771323, 1.4330640847642324, - -0.6075926305809934, -1.8295061379841517, 0.5375950122938771, -0.668826591213622 }, - }, - { - { 30.10531167057931, -3.4682674523063, -4.122859779991433, -2.4814276275979648, - -3.3693361452179884, -0.08565925201122448, -0.8401905476029238, - 3.8121277138883807 }, - { -0.028543509160374403, 1.4086455375421374, 7.027677724316744, -0.8029732853849363, - 0.21508257722145652, -1.2788348742349065, 0.7592532617840794, 0.09119198095300907 }, - { 2.4233994453629304, -3.012051635580196, 1.1558640736544619, -4.573157481233569, - 4.20161895289186, -1.204816459987149, -1.6145773913793897, -1.5849104998999013 }, - { -5.4157739368994235, -3.7882855945306204, 2.7772761792419294, -2.4584113040850095, - -2.069213530724667, -0.5478843826867934, -1.6160901693215453, -0.6568154923833014 }, - { -0.43647261868644627, -5.5334868871320335, 0.40365819731692065, 2.150018598987577, - -1.2004536158862775, 2.4388808649611087, 0.23590045802305715, -1.9545129970611796 }, - { 1.6309381678509949, -1.9880205240060747, -1.7739681748314111, -0.29204772903330056, - 0.8738754999325199, 0.2691425045145516, -1.2056513315502992, 1.071397699534742 }, - { 4.698703675388435, -0.7957304428932623, -4.7711315599977855, 0.09057617743728708, - 1.4691381287521628, -0.30099768897637336, -2.8594113121092493, 2.9571503367352006 }, - { 1.729635373900504, 1.4043331763077305, 0.4195010449027432, 2.9588786679014305, - -0.579121181023122, -2.422844062570481, -0.8625209856866667, 0.0362011748846961 }, - }, - }, - { - { - { 1.8880869312050084, 0.17014705148566178 }, - { 0.1511761270942118, 1.1057152930560752 }, - }, - { - { 1.3593048698788563, -0.5220741523649461 }, - { 0.1997741131437576, -0.6596574766514716 }, - }, - }, - { - { - { 0.5726217776053402, -0.3670970767855093 }, - { 0.09861077665339368, -0.05460772804233427 }, - }, - { - { 1.104719693762091, -0.9911964637382618 }, - { -0.35545870684141456, 0.4386470014173758 }, - }, - } + double[][][][] expectedOutputs = { + {{{2.810273443180434, 0.5315583899556339}, {-0.20450648028789575, -0.43732964918644013},}, + {{1.896089282928628, 1.1318539117217823}, {-0.03276314452169382, 0.3371527815133526},},}, + {{{8.029586903023583, 1.4783572816608272, 0.6711701068551449, -2.0728968306396376}, + {0.5021953651101563, -2.5648553213785314, 0.29423626194475117, 0.4012601985906169}, + {-1.2849021209400728, 0.6671323763730415, -0.6695725808397565, -1.1712843044117673}, + {2.108868551493044, 1.0843197356522687, 1.0867961189703699, -0.35053844891456276},}, + {{7.05912751036381, 0.5291423797442222, 0.4427197585821442, -0.755496055817138}, + {-0.30418687059677985, -2.2598935800730793, 0.6047902140223604, -1.5847243484575326}, + {0.25274774363624575, 0.05354074350183502, -0.46293891370557894, -1.4928446525217676}, + {0.04102445584853265, 1.2150529213724437, -0.3501635847389326, -1.0905422962407672},},}, + {{{6.568215549889579, -1.2298598996976158, 2.2138636830117013, -1.3116154891143874}, + {-1.1345702156384125, 0.1358688749140914, -0.869135880783189, -1.2945083094997623}, + {-0.23490362681105115, 1.8841337467288004, -1.290356324016746, -0.7998632424301513}, + {0.057854280160745564, 1.3009331869650396, 0.4577001084229444, 0.4609374698297427},}, + {{7.6362507070770524, -0.5021300289612729, -0.30830053219433307, -0.40162315416311345}, + {0.00532353080901915, 1.0369754448667339, -2.483776588979377, -0.016217813328190278}, + {-1.9062644821386074, -0.3728019954908228, -0.3037464465492685, -0.527450990728018}, + {0.46711047881499734, 0.4414987825805363, -1.1273549935419012, 1.4850074622342282},},}, + {{{26.68609298551157, -1.3338953635792201, 0.4020594424454147, 0.7401299103272476, -2.810554548459751, + -1.2778878008097723, -2.434717541683333, 1.8448958370317132}, + {1.5124772568141867, 2.997435519224954, 0.850391793491705, -1.409268535773998, 0.7298732550934647, + -1.0941883198448044, -0.4717054661204718, 2.2295601902248148}, + {0.9413326138415741, -3.5206438979732253, -1.860288065902871, -2.154762617920012, 1.4998965860283968, + 4.037874687522323, -2.97033202289703, -1.0678593057556733}, + {1.6739716993045417, -0.05711258741013203, -2.3676553799709974, 0.5456868972025865, 0.1948281831859553, + 0.7526107332947336, 5.065447419515738, 0.020456210203669833}, + {-0.6956713490957522, -1.8296524399484828, -3.1697164039855377, 2.237563147861894, 1.4551539353995173, + -0.19554352732365277, -0.4636431658812785, 2.8849067509584243}, + {1.2874212252086117, -2.007455023954, 0.41828409822144785, 2.1560540097992273, -0.9444730547029133, + -1.9570758407351865, -2.7449964653128944, -2.1931578153825004}, + {-1.1727630961580466, -0.4626944940820328, -0.6026604126017501, -1.147197715423295, -4.969175678925014, + -1.8568706158045902, -4.704146412253633, 3.2664484067588457}, + {-2.0576765512500503, 0.06751753454362419, 0.412051207702436, 0.026723509175442417, 0.8659815323685047, + 0.6298326096441218, -1.0225568132581624, -1.5743660274808011},}, + {{35.87455968396184, -1.5248270601889629, -3.804141676368115, 1.8347304094358663, -2.5797852364328016, + 4.556415677676634, -2.059620257585239, 3.3259090771947486}, + {3.8330193771856442, 3.96923392685639, -2.4257908732841234, -0.5143874873871535, 2.544553958429333, + 5.542586595696317, 4.951043571220989, -1.6001504185486546}, + {1.572704438375391, 2.68337930664312, -0.8033985360422281, -2.6558624309276784, -0.40085422767947554, + -4.000393284174368, -4.331102876667192, 3.506899629433532}, + {-2.4680885926531975, -0.8695810917508298, 2.9456617602301707, -0.24732914730729616, 0.3430542822308015, + 3.485849838657556, 3.9649335464206823, -1.680341105583727}, + {-0.22764415191794996, -0.4951316522431507, 4.061903443125903, -0.6424984523403212, + -0.12529040791295087, 5.354293685375573, 0.040525918509912495, 1.0133620066630307}, + {0.6469092912664327, -1.9089096532567051, -3.0326019092676626, -3.6447657932795416, -2.122099108477178, + -0.7865053946835596, -0.1566125392915363, 1.984921157087272}, + {1.3348166868775824, -1.2189282274599857, 1.2479841062752444, -0.948271682640534, -3.975769852998465, + -1.562443050085742, 1.6163117823648547, -4.4062925310517995}, + {2.9675969104598394, -3.280556379469013, 2.3899027415100598, -1.6067363579675273, 0.7470324060379839, + -2.499741728611057, 2.986670707313758, 1.6217252291607327},},}, + {{{2.193079441145362, -0.41181283923662215}, {-0.14081174003275643, 0.9465030238449373},}, + {{1.2886784093486097, 0.15796905335413058}, {-0.7334983268453378, -0.593037891692984},},}, + {{{8.066703633447759, 0.35776587504447654, -0.8975456466084525, -1.3260971108489834}, + {1.2925550450271355, 0.7471970785662574, 1.8523195957640421, -0.09130567177735105}, + {0.75069086903558, 0.45287128407866684, 1.1193031551551829, 0.11343169446248846}, + {1.4750614801978492, -0.4474301227850148, 0.46932996702100105, -0.9214050900106854},}, + {{7.590515281648607, -0.08161444013345664, -0.18103075974871663, 0.9381669029060151}, + {-1.5770093406180952, -2.7484477676036216, 1.3742861198499816, -1.2015040141484536}, + {0.046726986603437215, -2.150769239162087, -0.1301370419904344, 2.067734397359103}, + {-1.057408338160801, -0.5695893624855741, -0.4947019509681271, -0.9002185882701961},},}, + {{{8.16568534963898, -1.440928805449021, -0.5466902254014514, 0.9947324514433773}, + {-0.6989972691812494, -1.187942183202632, -1.062436068043263, 0.09504092090567329}, + {-0.3794316462577032, -0.523294612518167, 2.7973454035250693, -1.9389358312439806}, + {0.8221904791733652, 1.051347229425449, -1.835143916223776, 0.0628803438524741},}, + {{8.414930607970902, 1.0408479366310592, -3.3339265443792727, 1.3220823252356668}, + {-0.390009888242036, -0.7239135908698271, -0.5199660526428092, 1.1500729000277474}, + {1.0954307864509207, -1.1232199355230432, -0.8940432401163299, 1.086489516763935}, + {0.590432616869855, -0.5703843580205217, -0.11315800207193116, 2.138244529504565},},}, + {{{35.97203267785346, 0.45863783607284137, 1.173056641524758, 0.7826325498969262, -0.7567714479514733, + 0.8900030139481167, 2.3340075668593485, 2.7783597078640225}, + {-1.8297969735165558, 2.273178623633307, -0.48372099384482214, -0.40852587671049156, 3.760728469728649, + -0.7610308335136011, 1.2919699452742253, 4.670040243629213}, + {4.8372690355107135, -1.3261208568775944, 1.46633732066654, -0.0992228827598397, -1.9223527786243282, + 0.44458038637838815, 1.5533185398931715, 0.24496224499509434}, + {1.6661974146730905, 3.8314516505325447, -1.589204977232685, -2.9696832529162926, 1.079869594848176, + 2.8153541217824465, 3.0882863865289134, -2.28027259208448}, + {-1.0570779237084942, 2.806843738194792, -1.4276915996739796, -3.4905691035061417, -2.230138814152109, + -2.872817891174476, 3.932782623656472, 1.1162539578269415}, + {4.289222018755856, 2.105500347856227, -1.0762009261508743, -1.6228254057953164, -2.190533559398732, + 1.1004383893378056, 1.1709877234229678, 3.1232851514701316}, + {-0.45942227177196493, -1.819905457869288, 1.4446474205856596, 0.5520635899236316, 1.3655329844463444, + 2.67212745690105, -0.57694981659637, -3.6005960797530303}, + {-1.743365079415264, -2.335615239269563, -1.8778197199771323, 1.4330640847642324, -0.6075926305809934, + -1.8295061379841517, 0.5375950122938771, -0.668826591213622},}, + {{30.10531167057931, -3.4682674523063, -4.122859779991433, -2.4814276275979648, -3.3693361452179884, + -0.08565925201122448, -0.8401905476029238, 3.8121277138883807}, + {-0.028543509160374403, 1.4086455375421374, 7.027677724316744, -0.8029732853849363, 0.21508257722145652, + -1.2788348742349065, 0.7592532617840794, 0.09119198095300907}, + {2.4233994453629304, -3.012051635580196, 1.1558640736544619, -4.573157481233569, 4.20161895289186, + -1.204816459987149, -1.6145773913793897, -1.5849104998999013}, + {-5.4157739368994235, -3.7882855945306204, 2.7772761792419294, -2.4584113040850095, -2.069213530724667, + -0.5478843826867934, -1.6160901693215453, -0.6568154923833014}, + {-0.43647261868644627, -5.5334868871320335, 0.40365819731692065, 2.150018598987577, -1.2004536158862775, + 2.4388808649611087, 0.23590045802305715, -1.9545129970611796}, + {1.6309381678509949, -1.9880205240060747, -1.7739681748314111, -0.29204772903330056, 0.8738754999325199, + 0.2691425045145516, -1.2056513315502992, 1.071397699534742}, + {4.698703675388435, -0.7957304428932623, -4.7711315599977855, 0.09057617743728708, 1.4691381287521628, + -0.30099768897637336, -2.8594113121092493, 2.9571503367352006}, + {1.729635373900504, 1.4043331763077305, 0.4195010449027432, 2.9588786679014305, -0.579121181023122, + -2.422844062570481, -0.8625209856866667, 0.0362011748846961},},}, + {{{1.8880869312050084, 0.17014705148566178}, {0.1511761270942118, 1.1057152930560752},}, + {{1.3593048698788563, -0.5220741523649461}, {0.1997741131437576, -0.6596574766514716},},}, + {{{0.5726217776053402, -0.3670970767855093}, {0.09861077665339368, -0.05460772804233427},}, + {{1.104719693762091, -0.9911964637382618}, {-0.35545870684141456, 0.4386470014173758},},} - }; + }; - @Test - public void testFft2dWithGeneratedData() { - for (int testIndex = 0; testIndex < reInputs.length; testIndex++) { - double[] re = reInputs[testIndex]; - double[] im = imInputs[testIndex]; - double[][][] expected = expectedOutputs[testIndex]; + @Test + public void testFft2dWithGeneratedData() { + for(int testIndex = 0; testIndex < reInputs.length; testIndex++) { + double[] re = reInputs[testIndex]; + double[] im = imInputs[testIndex]; + double[][][] expected = expectedOutputs[testIndex]; - int sideLength = (int) Math.sqrt(re.length); + int sideLength = (int) Math.sqrt(re.length); - // Your FFT implementation - fft(re, im, sideLength, sideLength); + // Your FFT implementation + fft(re, im, sideLength, sideLength); - // Assert results - for (int i = 0; i < sideLength; i++) { - for (int j = 0; j < sideLength; j++) { - assertComplexEquals("Mismatch at [" + i + "][" + j + "]", - expected[0][i][j], expected[1][i][j], - re[i * sideLength + j], im[i * sideLength + j]); - } - } - } - } + // Assert results + for(int i = 0; i < sideLength; i++) { + for(int j = 0; j < sideLength; j++) { + assertComplexEquals("Mismatch at [" + i + "][" + j + "]", expected[0][i][j], expected[1][i][j], + re[i * sideLength + j], im[i * sideLength + j]); + } + } + } + } - @Test - public void testIfft2dWithGeneratedData() { - for (int testIndex = 0; testIndex < reInputs.length; testIndex++) { - double[] re = reInputs[testIndex]; - double[] im = imInputs[testIndex]; - double[][][] expected = expectedOutputs[testIndex]; + @Test + public void testIfft2dWithGeneratedData() { + for(int testIndex = 0; testIndex < reInputs.length; testIndex++) { + double[] re = reInputs[testIndex]; + double[] im = imInputs[testIndex]; + double[][][] expected = expectedOutputs[testIndex]; - int sideLength = (int) Math.sqrt(re.length); + int sideLength = (int) Math.sqrt(re.length); - // Your IFFT implementation - ifft(re, im, sideLength, sideLength); + // Your IFFT implementation + ifft(re, im, sideLength, sideLength); - // Assert results - for (int i = 0; i < sideLength; i++) { - for (int j = 0; j < sideLength; j++) { - assertComplexEquals("Mismatch at [" + i + "][" + j + "]", - expected[0][i][j], expected[1][i][j], - re[i * sideLength + j], im[i * sideLength + j]); - } - } - } - } + // Assert results + for(int i = 0; i < sideLength; i++) { + for(int j = 0; j < sideLength; j++) { + assertComplexEquals("Mismatch at [" + i + "][" + j + "]", expected[0][i][j], expected[1][i][j], + re[i * sideLength + j], im[i * sideLength + j]); + } + } + } + } } From 26fcd20f58eab7b86c766b7d3427cf3ee881aac0 Mon Sep 17 00:00:00 2001 From: Jessica Priebe Date: Tue, 23 Jan 2024 14:10:49 +0100 Subject: [PATCH 048/133] removed FourierTestLarge --- .../component/matrix/FourierTestLarge.java | 1380 ----------------- 1 file changed, 1380 deletions(-) delete mode 100644 src/test/java/org/apache/sysds/test/component/matrix/FourierTestLarge.java diff --git a/src/test/java/org/apache/sysds/test/component/matrix/FourierTestLarge.java b/src/test/java/org/apache/sysds/test/component/matrix/FourierTestLarge.java deleted file mode 100644 index 478ba0c8aa2..00000000000 --- a/src/test/java/org/apache/sysds/test/component/matrix/FourierTestLarge.java +++ /dev/null @@ -1,1380 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package org.apache.sysds.test.component.matrix; - -import org.junit.Test; - -import static org.junit.Assert.assertTrue; -import static org.apache.sysds.runtime.matrix.data.LibMatrixFourier.fft; -import static org.apache.sysds.runtime.matrix.data.LibMatrixFourier.ifft; - -public class FourierTestLarge { - int progressInterval = 5000; - - // prior to executing the following tests it is necessary to run the Numpy - // Script in FourierTestData.py - // and add the generated files to the root of the project. - @Test - public void testFftWithGeneratedData() { - // Define the input data array (first 1000 inputs) - double[][] inputData = { - {0.307856129173271, 0.08921910198690264, 0.618009230694374, 0.5313115890139828, 0.8321948342164646, - 0.5931468404266381, 0.6183643824098176, 0.09098483235809818, 0.020002539689180643, 0.9179524283672298, - 0.6092688826975122, 0.03765800262802799, 0.6583959135030817, 0.346267450443292, 0.9794472087243126, - 0.8642963614250007}, - {0.997301378000895, 0.3502640931966453, 0.1954636101454681, 0.10391072327500839, 0.21327031067780433, - 0.0643272237899376, 0.8221952489149892, 0.9534348411569122, 0.9534997101452614, 0.5141779472770125, - 0.2306040802116438, 0.8386736679248497, 0.0981318516277605, 0.8074595469184841, 0.254078430810886, - 0.5996117331901718}, - {0.870957838868506, 0.912745725021149, 0.5345683140982496, 0.18114400014042686, 0.5931978577526508, - 0.14192088683195558, 0.9208741124169134, 0.6525015591512732}, - {0.29788916579478153, 0.27748600931023504, 0.4853091738976063, 0.8567045012601394}, - {0.6352092395258702, 0.43145575925660073}, {0.5617699291201627, 0.21489241033852313}, - {0.6576030646834683, 0.9290768001953902, 0.09848637471541899, 0.06425572022630976, 0.5396213600686569, - 0.22085716679561374, 0.7272917506111063, 0.14096681648120335, 0.003323239715782522, 0.29724749937056516, - 0.2356003703231878, 0.4343299880624373, 0.6875247706424916, 0.8485613871894802, 0.9773614895293514, - 0.48945201661534776}, - {0.8821994027742829, 0.21163278841593125, 0.4932536463151129, 0.48650972337895804, 0.7786999918312242, - 0.14154217886198583, 0.050381939456006886, 0.4817754647240189, 0.25197827244265536, 0.19196634342654173, - 0.2248835944599762, 0.09610110952097772, 0.9234776641814892, 0.8345039754950259, 0.9145560074968945, - 0.2448292011827906}, - {0.7385591035373894, 0.6934429707180507, 0.20469771060726105, 0.7146443181329221}, - {0.09423365095676517, 0.4200805363713518, 0.8898189099184024, 0.15185704349763007, 0.976019182222216, - 0.26121755769518185, 0.07688163464324083, 0.5605512821290499, 0.01968958370279128, 0.02586318786500097, - 0.13903676787373986, 0.29289492175451814, 0.9598461594746724, 0.06685602573191851, 0.8132935172237855, - 0.07525352129151597}, - {0.06574341421208751, 0.8492596587119035, 0.8065274870296378, 0.27175430100069153, 0.2954082423553953, - 0.3563055835463892, 0.5417770631268415, 0.9615039909442821}, - {0.04211428054995747, 0.7911133121974274, 0.2118116815925455, 0.483908183679128, 0.9084187853511388, - 0.6796408581806436, 0.10941522676794624, 0.7455635937529194, 0.620135811235268, 0.7769721780197776, - 0.7167767032200226, 0.10390503304337428, 0.6407001194530335, 0.26083464261776423, 0.5806294628171111, - 0.2518664967318035}, - {0.18129008646882727, 0.8669649844463896, 0.028246543418389947, 0.48363667274187305, 0.7943230293132295, - 0.6961876877536417, 0.4275685366852513, 0.45634721662560607, 0.1005692992795224, 0.9050284283882628, - 0.3146718845340458, 0.7861414010588036, 0.4770427042197599, 0.1462914941041602, 0.3965644055730563, - 0.9153636362926348}, - {0.8161580934614122, 0.18477962701579598}, - {0.08028451743536968, 0.24496797132982906, 0.0849486774071061, 0.7881678651794348, 0.32208849264834005, - 0.027946650916384486, 0.6481634237831947, 0.07243767209378071, 0.5807489567390403, 0.852034426396901, - 0.7611516595374496, 0.05624561395049921, 0.2909447900344724, 0.02896220325285881, 0.6042269314873742, - 0.709088439526459}, - {0.9419390726681351, 0.25416475350108325, 0.3668196423318234, 0.0004937686334369751, 0.45115568384401916, - 0.15272066875461188, 0.13006769168951093, 0.008318934989370197, 0.7881465440955732, 0.933662847575916, - 0.08386521463152519, 0.6529529591688511, 0.4473354453449845, 0.31327384554825755, 0.5058970453705052, - 0.870525057224827}, - {0.5505180216822709, 0.5781501773859546}, - {0.8956572090779427, 0.8299050830853423, 0.1424103295292285, 0.13728455802847184, 0.8000171582078991, - 0.6298805318267743, 0.46391319943080855, 0.5663723332416153}, - {0.9683653719909785, 0.2232641902635809, 0.6148539423093378, 0.37453351154356274}, - {0.7067035754354158, 0.0663739473919116}, - {0.47086229838998894, 0.8960946381686666, 0.9533858690069733, 0.5176638484634724, 0.015341863574319992, - 0.09419290215121512, 0.7068849515138174, 0.2050445924658565}, - {0.5785763844543972, 0.9176187041034242}, - {0.5241184105276374, 0.2509183778867127, 0.12853860707933074, 0.9590386361137108}, - {0.8571553248185413, 0.6263814942317649, 0.95881731622584, 0.310415053009538, 0.4775273408541796, - 0.6329378690509789, 0.7178046354589865, 0.9923541273411732, 0.44017806754392763, 0.2522486894328567, - 0.12849993855560982, 0.8988211375767242, 0.3617603937666176, 0.10711880543199137, 0.9905379056406248, - 0.8906739530932947}, - {0.8536820346534935, 0.5656301408899768, 0.24077798818075857, 0.34595328041358364, 0.8821071518415022, - 0.5357200569496784, 0.13935877170910727, 0.2502969398391611}, - {0.7293440752321705, 0.335697322075603, 0.7609624135786428, 0.5430882990642741, 0.7903417394181252, - 0.09592791445258453, 0.8251861573566924, 0.5242152290578941, 0.5274752317660253, 0.5978730300121011, - 0.9257445772312801, 0.23083171339790576, 0.4992817006893182, 0.7009970366772414, 0.09991780231136771, - 0.19590332619221507}, - {0.45683639103550133, 0.4127698308179104, 0.15849785349035195, 0.23820398589917613, 0.42556319726902625, - 0.3902556783233726, 0.44922620996988205, 0.47889491731179334, 0.31088942492181826, 0.8561445618860377, - 0.35456306768933543, 0.6656974246709467, 0.60674113513703, 0.4713576352952902, 0.7389009344705372, - 0.8211538788821755}, - {0.18081479839397807, 0.47100279012993196}, - {0.21954540433093261, 0.40458389194582534, 0.7208723075287345, 0.8846816526712216, 0.5327808546064456, - 0.8864372849612085, 0.05751443204835982, 0.807453849418354, 0.9593743129540142, 0.6715092386060035, - 0.7003192281171006, 0.5476351764742082, 0.7413425202579201, 0.3598546138493517, 0.1710023879149254, - 0.3477614210012976}, - {0.04105479173235693, 0.2790129805889898}, - {0.31270679654880107, 0.6563226858676515, 0.065309659982147, 0.9594045059676773, 0.41461631741634075, - 0.8549726106226234, 0.6885625924540151, 0.34077133669639903}, - {0.05216212273283238, 0.1663159578169714, 0.6755000798755829, 0.8410660871499066}, - {0.43067498044817587, 0.38256389857433204}, - {0.637327693590928, 0.3133763054769817, 0.016610252725152153, 0.32652177024153395, 0.009691976865415008, - 0.23600780862854676, 0.8836055793679196, 0.45244777988456386}, - {0.5906578759363037, 0.7944662801327856, 0.17863645183223031, 0.32277739351179, 0.3231823947395689, - 0.4034735202642745, 0.06399390314662634, 0.17540386741349312}, - {0.17815921306628235, 0.8585869087579457}, {0.5871751868409761, 0.6534562814682419}, - {0.39586773608713244, 0.7387586667130125, 0.19184209782072326, 0.10410270481271588, 0.9951764576402166, - 0.23388910739629953, 0.7647794115198386, 0.8052705154780063}, - {0.6585162649365032, 0.711175495841072, 0.4676580282401064, 0.8826289448730669, 0.82861818848699, - 0.9426098006296768, 0.9615519675050708, 0.40172361372445387}, - {0.554938210317202, 0.45311686056607847, 0.848278396952335, 0.9663886131569552, 0.993175945037519, - 0.4321940233579258, 0.9786877966394255, 0.22383267497846604}, - {0.14832468791702524, 0.7911386648581192, 0.4342135601328583, 0.39064350292654393, 0.00154990539545663, - 0.7056390559656973, 0.5449016159778458, 0.44995063726730145}, - {0.6787570562911824, 0.2108141544985952, 0.36959074400723846, 0.6065094525663848, 0.14370437302298023, - 0.7471705670569604, 0.3991987649234662, 0.6377650280189965, 0.7966311569652879, 0.6699688376083757, - 0.11046232705364989, 0.0343797538976689, 0.4263281825639861, 0.06018487827135577, 0.27543446279089245, - 0.6307832714073219}, - {0.007401290917390502, 0.8640389112554114, 0.08779720334707941, 0.6587228816523323, 0.21985520800643954, - 0.4963969113562372, 0.25041837572771375, 0.2996763341721056}, - {0.9283706112012573, 0.9008656544411213, 0.01039871402142889, 0.637525445174257}, - {0.838047562953646, 0.9020667732919446}, - {0.7455441724472716, 0.5020772412030806, 0.10951344204683389, 0.30933274344662487}, - {0.8180656768575455, 0.8666279680604059, 0.42583580021283085, 0.1433193988956274, 0.8669332518159855, - 0.7414686722465978, 0.5939337651104747, 0.6789552509532184}, - {0.48808916431556837, 0.8241477106539906, 0.7262098103482119, 0.899834357237205, 0.8834046134657804, - 0.6029189784057519, 0.46118570354302213, 0.20904657865994403, 0.17528071980731652, 0.9766278629190644, - 0.2643652212919221, 0.22466069704047253, 0.5868640515110155, 0.40572150873462176, 0.012091970132293572, - 0.7702657347835162}, - {0.8296574984897637, 0.2009561060794306, 0.4599409596490742, 0.2981967624462212}, - {0.9517083930235997, 0.9818426792525043}}; - - double[][][] outputData = { - {{8.114375727757187, 0.5925913961852104, -0.8416569218183314, -0.22284951774343884, -1.0066402879440184, - 0.27554774792062964, -1.483807235895858, 0.5061247315739602, 1.1727025144588428, 0.5061247315739602, - -1.483807235895858, 0.27554774792062964, -1.0066402879440184, -0.22284951774343884, -0.8416569218183314, - 0.5925913961852104}, - {0.0, 0.00425759907320819, 0.5957854277534866, 2.186428829721094, -0.4223350357989528, - 1.3405434768573832, -0.14528152773100134, -0.14643207093697122, 0.0, 0.14643207093697122, - 0.14528152773100134, -1.3405434768573832, 0.4223350357989528, -2.186428829721094, - -0.5957854277534866, -0.00425759907320819}}, - {{7.9964043972637295, -0.85788941138668, 2.065867366646952, 0.2645073319462676, 0.759861880368734, - 0.6762306422958229, 1.2129304850342315, 0.09235810856712429, -0.46731515619431363, 0.09235810856712429, - 1.2129304850342315, 0.6762306422958229, 0.759861880368734, 0.2645073319462676, 2.065867366646952, - -0.85788941138668}, - {0.0, 0.8007124150209053, 1.0870614472850983, -1.0027520373092829, 0.7594021543648628, - -0.47928657559005056, -0.21335053145242822, 1.784731712940313, 0.0, -1.784731712940313, - 0.21335053145242822, 0.47928657559005056, -0.7594021543648628, 1.0027520373092829, - -1.0870614472850983, -0.8007124150209053}}, - {{4.8079102942811245, 1.156115577646565, 0.008713270105993765, -0.6005956154148545, 1.0312859519915147, - -0.6005956154148545, 0.008713270105993765, 1.156115577646565}, - {0.0, 0.17455045446816958, -0.2210210525614047, -0.5980611421691582, 0.0, 0.5980611421691582, - 0.2210210525614047, -0.17455045446816958}}, - {{1.9173888502627623, -0.18742000810282478, -0.3509921708779866, -0.18742000810282478}, - {0.0, 0.5792184919499044, 0.0, -0.5792184919499044}}, - {{1.0666649987824708, 0.20375348026926943}, {0.0, 0.0}}, - {{0.7766623394586858, 0.3468775187816395}, {0.0, 0.0}}, - {{7.35155981522581, 1.7384348144213493, -0.36205061306792863, 0.7115391307931807, -0.1506675500686654, - 0.4372769750045813, -0.7703890395558666, -0.27013162034836824, 0.5020650253531165, -0.27013162034836824, - -0.7703890395558666, 0.4372769750045813, -0.1506675500686654, 0.7115391307931807, -0.36205061306792863, - 1.7384348144213493}, - {0.0, 1.2350796850853472, 1.3528374749311034, -0.5177322782271997, -1.166738312165751, - -0.7694859455764832, -1.3882955152725982, 0.3917123754407248, 0.0, -0.3917123754407248, - 1.3882955152725982, 0.7694859455764832, 1.166738312165751, 0.5177322782271997, -1.3528374749311034, - -1.2350796850853472}}, - {{7.208291303963872, 1.6448982875247267, -0.8709621365176501, -1.2546616319678878, 1.1532801435016609, - 0.9134446382882846, -0.26503782507390033, 1.2172032274813867, 1.8305697339514122, 1.2172032274813867, - -0.26503782507390033, 0.9134446382882846, 1.1532801435016609, -1.2546616319678878, -0.8709621365176501, - 1.6448982875247267}, - {0.0, 0.7473959934264138, 0.7534008934360946, -0.07634193189820554, -0.07042978739273931, - -0.6293807074039233, 0.25979948108047013, -0.3847534714803642, 0.0, 0.3847534714803642, - -0.25979948108047013, 0.6293807074039233, 0.07042978739273931, 0.07634193189820554, - -0.7534008934360946, -0.7473959934264138}}, - {{2.3513441029956232, 0.5338613929301284, -0.4648304747063223, 0.5338613929301284}, - {0.0, 0.02120134741487134, 0.0, -0.02120134741487134}}, - {{5.823393482351781, 0.9136499207248283, -1.603500573737872, -0.7020472269919936, 0.13075774669727602, - -1.2520745980052537, -2.040383640336792, 1.3386481732883144, 2.1142453296794463, 1.3386481732883144, - -2.040383640336792, -1.2520745980052537, 0.13075774669727602, -0.7020472269919936, -1.603500573737872, - 0.9136499207248283}, - {0.0, -0.4121748237807462, -0.08693254435692731, -0.7861482143975829, 0.3065394610092609, - -0.7981716440636941, 0.19042850749330453, -0.35950616245668343, 0.0, 0.35950616245668343, - -0.19042850749330453, 0.7981716440636941, -0.3065394610092609, 0.7861482143975829, - 0.08693254435692731, 0.4121748237807462}}, - {{4.1482797409272285, 0.6066330243002018, -0.9871528935889964, -1.0659626805868174, -0.7293673274793042, - -1.0659626805868174, -0.9871528935889964, 0.6066330243002018}, - {0.0, -0.12559491018544305, 0.02769304968668118, 0.40390593762014965, 0.0, -0.40390593762014965, - -0.02769304968668118, 0.12559491018544305}}, - {{7.923806369209862, -1.0597881688755275, -0.15343866443479803, -0.701824885719718, 0.5927359221917725, - -0.40648735694530763, -1.620298961603096, -0.14398571120068893, -0.2638022272358147, - -0.14398571120068893, -1.620298961603096, -0.40648735694530763, 0.5927359221917725, -0.701824885719718, - -0.15343866443479803, -1.0597881688755275}, - {0.0, -0.5098005943518916, -0.3926881055602594, 0.7944914010859094, -0.9233176838083876, - -1.1214718762239086, 0.08439928489476206, -1.3548892080692885, 0.0, 1.3548892080692885, - -0.08439928489476206, 1.1214718762239086, 0.9233176838083876, -0.7944914010859094, - 0.3926881055602594, 0.5098005943518916}}, - {{7.9762380109034545, -0.08102561731855723, -0.2601631725122897, 1.2537848144634045, 0.38617374907059565, - -0.6434301753632343, -1.7188495230569896, -0.2064458730243935, -2.53568503191929, -0.2064458730243935, - -1.7188495230569896, -0.6434301753632343, 0.38617374907059565, 1.2537848144634045, -0.2601631725122897, - -0.08102561731855723}, - {0.0, -0.17500593841444329, -0.10397394228803791, 1.0518049218488463, 0.02701633202646292, - 0.05602413235726633, -1.0664029708997815, 0.0983345724678551, 0.0, -0.0983345724678551, - 1.0664029708997815, -0.05602413235726633, -0.02701633202646292, -1.0518049218488463, - 0.10397394228803791, 0.17500593841444329}}, - {{1.000937720477208, 0.6313784664456162}, {0.0, 0.0}}, - {{6.152408291718494, -0.701864450013982, 0.7389893051923667, -0.6570737426544427, -0.8244239353579019, - 0.6745758755808154, -0.6429889222091717, -1.3174954401270726, 0.5927066064261997, -1.3174954401270726, - -0.6429889222091717, 0.6745758755808154, -0.8244239353579019, -0.6570737426544427, 0.7389893051923667, - -0.701864450013982}, - {0.0, 0.2166164561807843, -0.37363526340890696, 1.9069743954629836, 0.47202833885420037, - 0.9505271452772353, -1.1862153000609332, -0.6152559835494934, 0.0, 0.6152559835494934, - 1.1862153000609332, -0.9505271452772353, -0.47202833885420037, -1.9069743954629836, - 0.37363526340890696, -0.2166164561807843}}, - {{6.9013391753724305, 0.6001789336263573, 1.5013874922440678, 0.21234328277453474, 1.5419271519293472, - -0.8364191839302667, 0.1618014829053418, 0.6390670818196226, 0.5291135045797226, 0.6390670818196226, - 0.1618014829053418, -0.8364191839302667, 1.5419271519293472, 0.21234328277453474, 1.5013874922440678, - 0.6001789336263573}, - {0.0, 1.402962398507333, -0.1657532563195791, 1.1827153358128617, -0.12153139536338342, - 1.0437298788884946, -0.5363130165129142, 1.2792578955791045, 0.0, -1.2792578955791045, - 0.5363130165129142, -1.0437298788884946, 0.12153139536338342, -1.1827153358128617, - 0.1657532563195791, -1.402962398507333}}, - {{1.1286681990682257, -0.027632155703683714}, {0.0, 0.0}}, - {{4.465440402428082, 0.5404896430462359, 1.0893508383258046, -0.3492095413061488, 0.1385553900636749, - -0.3492095413061488, 1.0893508383258046, 0.5404896430462359}, - {0.0, 0.48347502888031313, -0.7561287236420293, -0.15953071092284696, 0.0, 0.15953071092284696, - 0.7561287236420293, -0.48347502888031313}}, - {{2.18101701610746, 0.35351142968164073, 0.9854216124931725, 0.35351142968164073}, - {0.0, 0.15126932127998183, 0.0, -0.15126932127998183}}, - {{0.7730775228273274, 0.6403296280435042}, {0.0, 0.0}}, - {{3.85947096373431, 0.8014953943534662, -1.1740666585564816, 0.10954547527787173, 0.4334790012358889, - 0.10954547527787173, -1.1740666585564816, 0.8014953943534662}, - {0.0, -1.034586268721768, -0.2675790993905529, -0.5415844337354562, 0.0, 0.5415844337354562, - 0.2675790993905529, 1.034586268721768}}, - {{1.4961950885578212, -0.339042319649027}, {0.0, 0.0}}, - {{1.8626140316073916, 0.3955798034483067, -0.5572999963934553, 0.3955798034483067}, - {0.0, 0.7081202582269981, 0.0, -0.7081202582269981}}, - {{9.64323205203265, 1.0222697551724542, 1.0324747401969991, 0.77067558102107, -0.6590386688977952, - -1.496670252648384, -0.11638342471365593, 1.3716339455533146, 0.2213297936960057, 1.3716339455533146, - -0.11638342471365593, -1.496670252648384, -0.6590386688977952, 0.77067558102107, 1.0324747401969991, - 1.0222697551724542}, - {0.0, -0.634301326905181, 0.9994818327031652, -0.7420494450419806, 1.473577412873138, - -0.18504033224304028, -0.24256873993315792, 0.3857755742440071, 0.0, -0.3857755742440071, - 0.24256873993315792, 0.18504033224304028, -1.473577412873138, 0.7420494450419806, - -0.9994818327031652, 0.634301326905181}}, - {{3.8135263644772612, -0.07491474109162899, 1.35565242660513, 0.01806450671561144, 0.4183255282924616, - 0.01806450671561144, 1.35565242660513, -0.07491474109162899}, - {0.0, -0.1902080867353592, -0.5050999775869104, 0.012630346207943427, 0.0, -0.012630346207943427, - 0.5050999775869104, 0.1902080867353592}}, - {{8.382787568513443, -0.6219859812329366, 0.025775381293649605, -0.2422386710017057, -0.06536820337234373, - 1.9046978726738621, -0.09138364751214496, -0.23299784657463904, 1.933719826653804, -0.23299784657463904, - -0.09138364751214496, 1.9046978726738621, -0.06536820337234373, -0.2422386710017057, - 0.025775381293649605, -0.6219859812329366}, - {0.0, -0.4421693421141475, -0.8962692960095064, -0.27841993134928456, -0.23645673550524116, - -0.06789283541651772, 0.6269367662742193, 0.9325979087338476, 0.0, -0.9325979087338476, - -0.6269367662742193, 0.06789283541651772, 0.23645673550524116, 0.27841993134928456, - 0.8962692960095064, 0.4421693421141475}}, - {{7.835696127070186, -0.013838185418746013, 0.3035453412810106, 0.36108371712932497, 0.098842082743269, - -0.20157362393768677, -0.8327023741784841, 0.4381159566818401, -0.8332596991032202, 0.4381159566818401, - -0.8327023741784841, -0.20157362393768677, 0.098842082743269, 0.36108371712932497, 0.3035453412810106, - -0.013838185418746013}, - {0.0, 1.2951778169532924, 0.667179370978872, 0.6934919236313549, 0.07342250044148102, - 0.36890779027205756, -0.6829530755425919, 0.24588193212198017, 0.0, -0.24588193212198017, - 0.6829530755425919, -0.36890779027205756, -0.07342250044148102, -0.6934919236313549, - -0.667179370978872, -1.2951778169532924}}, - {{0.65181758852391, -0.2901879917359539}, {0.0, 0.0}}, - {{9.012668576685904, -1.3888872356007793, -0.4114927520171256, -0.937566206657709, 0.8033347365401919, - -0.7316542605934145, 0.2210854368582879, 0.09879206835957632, -0.807165681169038, 0.09879206835957632, - 0.2210854368582879, -0.7316542605934145, 0.8033347365401919, -0.937566206657709, -0.4114927520171256, - -1.3888872356007793}, - {0.0, -0.5973815143710736, -1.2682664037495632, 0.009556220704126317, 0.26514707020269235, - 0.29524978930704304, 1.1170830276155366, -1.1459346083740547, 0.0, 1.1459346083740547, - -1.1170830276155366, -0.29524978930704304, -0.26514707020269235, -0.009556220704126317, - 1.2682664037495632, 0.5973815143710736}}, - {{0.32006777232134676, -0.2379581888566329}, {0.0, 0.0}}, - {{4.292666505555655, -0.679815938802624, -0.02654913847102025, 0.47599689706754467, -1.3302757727530472, - 0.47599689706754467, -0.02654913847102025, -0.679815938802624}, - {0.0, 0.3262799322896599, -0.21111945382619868, -0.9202259326540763, 0.0, 0.9202259326540763, - 0.21111945382619868, -0.3262799322896599}}, - {{1.7350442475752934, -0.6233379571427505, -0.2797198423584627, -0.6233379571427505}, - {0.0, 0.6747501293329352, 0.0, -0.6747501293329352}}, - {{0.8132388790225079, 0.04811108187384383}, {0.0, 0.0}}, - {{2.875589166781041, 0.7713866408436004, -0.25319616163672876, 0.48388479260742556, 0.2188818383177884, - 0.48388479260742556, -0.25319616163672876, 0.7713866408436004}, - {0.0, 0.9013306732173781, 0.22958543602056936, -0.8326599800681568, 0.0, 0.8326599800681568, - -0.22958543602056936, -0.9013306732173781}}, - {{2.8525916869770724, 0.43974029342312404, 0.6712099156970159, 0.09521066897034558, -0.539650435667614, - 0.09521066897034558, 0.6712099156970159, 0.43974029342312404}, - {0.0, -0.49532500025494997, -0.6997585394717769, -0.266039902883742, 0.0, 0.266039902883742, - 0.6997585394717769, 0.49532500025494997}}, - {{1.036746121824228, -0.6804276956916634}, {0.0, 0.0}}, - {{1.240631468309218, -0.06628109462726572}, {0.0, 0.0}}, - {{4.229686697467945, 0.25348848112557965, 0.4344226843867871, -1.452105924231748, 0.46564470866787655, - -1.452105924231748, 0.4344226843867871, 0.25348848112557965}, - {0.0, 0.7117411383627559, -0.06327455381858982, -0.4341334890354749, 0.0, 0.4341334890354749, - 0.06327455381858982, -0.7117411383627559}}, - {{5.8544823042369405, -0.6738021106296501, 0.057924457678316, 0.3335982635286765, -0.02179340589959944, - 0.3335982635286765, 0.057924457678316, -0.6738021106296501}, - {0.0, 0.3174912848162346, -0.36943273787322806, -0.6702965937136942, 0.0, 0.6702965937136942, - 0.36943273787322806, -0.3174912848162346}}, - {{5.450612521005906, -0.9485093939451186, -0.2788520382370394, 0.07203392450448454, 1.2995481768870563, - 0.07203392450448454, -0.2788520382370394, -0.9485093939451186}, - {0.0, -0.4094516196808049, 0.304910404211417, -0.670270419054986, 0.0, 0.670270419054986, - -0.304910404211417, 0.4094516196808049}}, - {{3.4663616304408476, 0.249168612623289, -0.8292405827982222, 0.04438095241984823, -1.2083820915944758, - 0.04438095241984823, -0.8292405827982222, 0.249168612623289}, - {0.0, 0.09216717947344956, -0.6561835806299711, -0.12920893221652552, 0.0, 0.12920893221652552, - 0.6561835806299711, -0.09216717947344956}}, - {{6.797683010944343, -0.49676461769680663, 1.4010987748295984, -0.2858605784410965, 0.8907344700681898, - -0.14132138994562243, 0.4096125405094099, 0.4524501833871039, -0.3974688757069753, 0.4524501833871039, - 0.4096125405094099, -0.14132138994562243, 0.8907344700681898, -0.2858605784410965, 1.4010987748295984, - -0.49676461769680663}, - {0.0, -0.978354076595978, 0.5864810413074062, 0.3462260624882627, 0.22129906845508507, - 1.4529657578358368, 0.19732072800046563, -1.0021096194124273, 0.0, 1.0021096194124273, - -0.19732072800046563, -1.4529657578358368, -0.22129906845508507, -0.3462260624882627, - -0.5864810413074062, 0.978354076595978}}, - {{2.8843071164347096, -0.20637601439624492, -0.11095908015096312, -0.21853181978185315, -1.753362960437463, - -0.21853181978185315, -0.11095908015096312, -0.20637601439624492}, - {0.0, -0.35122522728194183, -0.40203660678721076, -0.6764675720432105, 0.0, 0.6764675720432105, - 0.40203660678721076, 0.35122522728194183}}, - {{2.4771604248380648, 0.9179718971798284, -0.5996217743926923, 0.9179718971798284}, - {0.0, -0.2633402092668643, 0.0, 0.2633402092668643}}, - {{1.7401143362455906, -0.0640192103382986}, {0.0, 0.0}}, - {{1.666467599143811, 0.6360307304004377, 0.04364762984439996, 0.6360307304004377}, - {0.0, -0.19274449775645575, 0.0, 0.19274449775645575}}, - {{5.1351397841526865, 0.41838515507659374, 0.6652293633502253, -0.5161203049934737, 0.2743972038409872, - -0.5161203049934737, 0.6652293633502253, 0.41838515507659374}, - {0.0, 0.4583487213357241, -0.7858219904581578, 0.1221527915404364, 0.0, -0.1221527915404364, - 0.7858219904581578, -0.4583487213357241}}, - {{8.510714682849697, 0.882363829984205, -0.34943439017669387, 0.018617453499083503, 0.669785843784231, - 0.588967002531606, -1.2643631715311279, -0.2387145079818872, -1.3157321740194359, -0.2387145079818872, - -1.2643631715311279, 0.588967002531606, 0.669785843784231, 0.018617453499083503, -0.34943439017669387, - 0.882363829984205}, - {0.0, -1.4735161576417974, -1.1800811497522834, 0.6456240585574142, -0.7056086929922909, - 1.3408042647661378, -0.14548643382264692, 0.40782629638598544, 0.0, -0.40782629638598544, - 0.14548643382264692, -1.3408042647661378, 0.7056086929922909, -0.6456240585574142, - 1.1800811497522834, 1.4735161576417974}}, - {{1.7887513266644897, 0.36971653884068945, 0.790445589613186, 0.36971653884068945}, - {0.0, 0.09724065636679058, 0.0, -0.09724065636679058}}, - {{1.933551072276104, -0.030134286228904683}, {0.0, 0.0}}}; - - long totalTime = 0; // Total time for all FFT computations - int numCalculations = 0; // Number of FFT computations - - for(int i = 0; i < inputData.length; i++) { - double[] re = inputData[i]; - double[] im = new double[re.length]; // Initialize with zeros - - double[][] expected = outputData[i]; - - long startTime = System.nanoTime(); - fft(re, im, 1, re.length); - long endTime = System.nanoTime(); - - totalTime += (endTime - startTime); - numCalculations++; - - double[][] actual = {re, im}; - - // Validate the FFT results - validateFftResults(expected, actual, i + 1); - } - - double averageTime = (totalTime / 1e6) / numCalculations; // Average time in milliseconds - System.out.println("Average execution time: " + String.format("%.8f", averageTime / 1000) + " s"); - } - - private void validateFftResults(double[][] expected, double[][] actual, int lineNumber) { - int length = expected[0].length; - for(int i = 0; i < length; i++) { - assertEquals("Mismatch in real part at index " + i + " in line " + lineNumber, expected[0][i], actual[0][i], - 1e-9); - assertEquals("Mismatch in imaginary part at index " + i + " in line " + lineNumber, expected[1][i], - actual[1][i], 1e-9); - } - } - - @Test - public void testIfftWithRealNumpyData() { - - // Define the input data array (real IFFT inputs) - double[][] inputData = { - {0.5398705320215192, 0.1793355360736929, 0.9065254044489506, 0.45004385530909075, 0.11128090341119468, - 0.11742862805303522, 0.7574827475752481, 0.2778193170985158, 0.9251562928110273, 0.9414429667551927, - 0.45131569795507087, 0.9522067687409731, 0.22491032260636257, 0.6579426733967295, 0.7021558730366062, - 0.7861117825617701, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0.6557180567930387, 0.7738395851954255, 0.5282681528342636, 0.6867068152297491, 0.032829567791427205, - 0.060095237489160236, 0.5250841288480065, 0.9356398294818203, 0, 0, 0, 0, 0, 0, 0, 0}, - {0.15785491881056868, 0.9056824935624028, 0.8375304868692421, 0.9188982121338262, 0, 0, 0, 0}, - {0.2362087514654716, 0.031154253034164303, 0.6157257211463447, 0.3773680629310894, 0, 0, 0, 0}, - {0.3751160998031162, 0.4253486725063852, 0.3535750705572219, 0.9557962271336204, 0, 0, 0, 0}, - {0.38741575855731003, 0.9967628087038197, 0.6755232743231949, 0.381124935406706, 0, 0, 0, 0}, - {0.7952407554893315, 0.5188862746399061, 0.4828070990424008, 0.41146079690881665, 0.5523554474649665, - 0.3371675982794765, 0.9086513990497248, 0.9480377757565142, 0.29262673918949955, 0.5029584519842616, - 0.1673523322593311, 0.909533552169132, 0.15086390797253646, 0.08903568696591746, 0.9762713649580028, - 0.79327533892987, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0.16166982209302416, 0.48331281957071315, 0.8297038690562623, 0.2481405411628217, 0.37652293557756644, - 0.35568361617736255, 0.9886708014513632, 0.029556032119152365, 0, 0, 0, 0, 0, 0, 0, 0}, - {0.822115700110782, 0.29037305812947556, 0, 0}, {0.1364777724494669, 0.4413020994649117, 0, 0}, - {0.4260028251864256, 0.2655565566693795, 0, 0}, - {0.8162743187254883, 0.6131080055615629, 0.016530179658639677, 0.3647951810730393, 0, 0, 0, 0}, - {0.32404367744317786, 0.9975863008660675, 0, 0}, - {0.9287511285137915, 0.9885317245671525, 0.29542406948511, 0.307667914659569, 0, 0, 0, 0}, - {0.6391321634628706, 0.8633641189940532, 0.4309541791982252, 0.9557087449945955, 0.3185873920535448, - 0.16533643465056203, 0.6461385746878021, 0.7043234939129138, 0, 0, 0, 0, 0, 0, 0, 0}, - {0.9801750501829359, 0.25789696071187607, 0, 0}, {0.22972137572909201, 0.5817904901405937, 0, 0}, - {0.7239381644147941, 0.7687362814620392, 0.0873707955450509, 0.9506567262739939, 0, 0, 0, 0}, - {0.6141463720447145, 0.7403205748478789, 0.44227773948538585, 0.7277081432710838, 0.9352081899539255, - 0.4240443645856953, 0.4346954941173261, 0.5879657145765288, 0.23766574473359614, 0.6530152865673962, - 0.11484140648508312, 0.14574807816162283, 0.9624874452393961, 0.5592097861022578, 0.005521469527721035, - 0.06956740990618893, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0.3838845675941237, 0.5088807724736699, 0, 0}, - {0.6983109361802508, 0.6244339329560505, 0.9008871130564603, 0.8932736022416019, 0.8727581351265782, - 0.9856438893255001, 0.5696671959058417, 0.15020089983612606, 0.058348906817813906, 0.7629908745245777, - 0.48442101642797164, 0.8502536763232881, 0.14290448979940018, 0.14311515061344016, 0.933512771559466, - 0.7512746845582662, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0.7556811638466671, 0.7893033739069542, 0.3459619060619262, 0.19402945604163735, 0, 0, 0, 0}, - {0.14095847557513064, 0.9354102907913319, 0.47692666603219047, 0.9655253211517809, 0, 0, 0, 0}, - {0.5132699837151409, 0.48338858084587877, 0.21082789930932544, 0.38487660081876207, 0.49257322976621676, - 0.4546677266078989, 0.8125433122155187, 0.1871279228630126, 0.9935603064559684, 0.14244380671166657, - 0.33692793538062416, 0.8184163712132382, 0.1909607709342982, 0.40792019329451423, 0.745094091124709, - 0.5706291972106916, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0.13418006969295326, 0.4945716623889531, 0.11760243909921375, 0.2570864674258849, 0.07735057384397426, - 0.019114456711389338, 0.9417433018717989, 0.9498903789723119, 0.15850190266259456, 0.2328071188590526, - 0.25865880162909693, 0.008950504888770583, 0.8898672604984333, 0.6296228316964232, 0.769087346766234, - 0.595502278059397, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0.6547029168282242, 0.4498064503056928, 0.5566645171023767, 0.8447764906827644, 0.8134333284372349, - 0.033684622580624546, 0.2968711640065672, 0.8285143785250315, 0.5791098493622432, 0.5979371998171618, - 0.3650614808824525, 0.8518268346228536, 0.8198089679249101, 0.9819781459749414, 0.9725187053011243, - 0.17296496382033655, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0.6448779915504477, 0.09112098044117456, 0.9263097426435519, 0.27812390544808974, 0.2760810901241125, - 0.7646817729525881, 0.911491047258462, 0.4005509961726136, 0.9694912938404336, 0.8886893035007664, - 0.6133994082666177, 0.3533649879581323, 0.24573475689499646, 0.8300954022435542, 0.010790385307265948, - 0.3607772174510355, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0.9753223707638033, 0.6956234223864403, 0.5023331089283625, 0.6507908760350136, 0.10653272730910857, - 0.4996211399988053, 0.8760128351901267, 0.602426660576998, 0, 0, 0, 0, 0, 0, 0, 0}, - {0.6504577559004796, 0.25145420305800725, 0.493505599054606, 0.4515689832358094, 0.14356101422612022, - 0.3341029115381504, 0.5008369992482713, 0.25927336770416753, 0.9144019406085643, 0.269776237000278, - 0.6382189705979336, 0.9554129674543379, 0.6887405590404585, 0.5039371147315215, 0.8252078215260031, - 0.10805307033951939, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0.41724967962818993, 0.2781633024698039, 0, 0}, - {0.4002455779217782, 0.564838604931205, 0.1953327862932963, 0.7012116008559239, 0.8022033779885992, - 0.2808573734656151, 0.3000946668377774, 0.17230898600893108, 0, 0, 0, 0, 0, 0, 0, 0}, - {0.6461124772516227, 0.48674094781923605, 0.10036448207277027, 0.015471823700216714, 0.7689797229739221, - 0.03476124705509731, 0.8330787816937497, 0.35390071912245424, 0.18458690293633118, 0.42355207544691875, - 0.5798363871357116, 0.4243527922467508, 0.2879869168805991, 0.13736279699966614, 0.26166696186760385, - 0.9787373283247385, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0.1398260006661618, 0.12153200522616103, 0.7121966216410146, 0.9133849754561364, 0.7314612511926722, - 0.739267240037694, 0.7546027553155675, 0.342420614369761, 0, 0, 0, 0, 0, 0, 0, 0}, - {0.22574862111952854, 0.5690849507265368, 0.631317280107177, 0.8629733195605777, 0.3899681673812473, - 0.8794702418181954, 0.1517842815507917, 0.1657877194968821, 0.8083699897670112, 0.4355578268421948, - 0.1969236012819674, 0.06184299476225663, 0.07860704151155451, 0.397292331637211, 0.3839838104251525, - 0.26205058162224937, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0.33135924721579535, 0.5366302683143123, 0.25432093749208906, 0.7498876975281418, 0, 0, 0, 0}, - {0.6134308716246728, 0.09706486959071292, 0.356584692623391, 0.9766274230749799, 0.24172086288049444, - 0.7974589582847552, 0.7896787540010787, 0.5664999509048404, 0, 0, 0, 0, 0, 0, 0, 0}, - {0.18439523648303413, 0.5938145715696269, 0.5138768707589609, 0.03744323769215385, 0.9335962783171388, - 0.5763983101382048, 0.2806801388811724, 0.40858218250589795, 0, 0, 0, 0, 0, 0, 0, 0}, - {0.6665104076212911, 0.12712939621992436, 0, 0}, {0.07848088184489588, 0.9037683971776713, 0, 0}, - {0.2961816890284832, 0.6548352902037954, 0.004297273252101497, 0.6379597126860397, 0.394953301386181, - 0.3591579315172063, 0.5200829702509112, 0.7348916802637337, 0, 0, 0, 0, 0, 0, 0, 0}, - {0.15333899851758237, 0.9557388547562318, 0, 0}, - {0.24490652495244614, 0.764767557933974, 0.7412612945684217, 0.8345076232268165, 0.9713064054627377, - 0.1666699320547278, 0.6385013568574381, 0.02837645523199317, 0, 0, 0, 0, 0, 0, 0, 0}, - {0.43636393937714724, 0.5006457049751213, 0.29183886078443877, 0.7771528617739111, 0.9760896495636339, - 0.4211692751284558, 0.46136604887195976, 0.4385215837142291, 0.6480247707507675, 0.2869034885339521, - 0.21139535714638436, 0.623852932281303, 0.10723939013625783, 0.7423978700202447, 0.8339592164587588, - 0.6541139999860581, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0.673783082139485, 0.08489915452638419, 0.4428704894374401, 0.6322989775504414, 0, 0, 0, 0}, - {0.435204198844909, 0.60467095443427, 0.43492988438024205, 0.45115625691681205, 0.6073353909030185, - 0.7354160659126683, 0.6584642868836427, 0.49841087464599554, 0.3218054140407358, 0.8547287810979488, - 0.25920067933895863, 0.3577532915633288, 0.17696516490870162, 0.9810705645133889, 0.08037125000861489, - 0.9406431999485564, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0.14276827960992966, 0.13654559645650455, 0, 0}, - {0.10449615216668384, 0.8809636163201752, 0.23757224642400443, 0.6033844272609862, 0.3180497932853712, - 0.8000641846428788, 0.36691139380180937, 0.9936551508162524, 0, 0, 0, 0, 0, 0, 0, 0}, - {0.5619461607033829, 0.05587776081117257, 0.8242650361208828, 0.31679799606054226, 0.67922806899631, - 0.31896202698295617, 0.47844249679108297, 0.43194377662374206, 0, 0, 0, 0, 0, 0, 0, 0}, - {0.5928888719853808, 0.8700201519901183, 0.909292241027492, 0.1017088272754374, 0.7250571447087572, - 0.3172141314203841, 0.05139597560789155, 0.8469852045420578, 0.744706896124228, 0.24878503090173887, - 0.3248378441207508, 0.6159251794660624, 0.46869738424898, 0.42094147190658915, 0.2078392223975123, - 0.17713383848172415, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0.8259324464059233, 0.9375338155913502, 0.5557276593014934, 0.017649722866079798, 0, 0, 0, 0}}; - - double[][][] outputData = { - {{0.5613143313659362, -0.020146500453061336, 0.07086545895481336, -0.05003801442765281, - -0.06351635451036074, -0.03346768844048936, 0.07023899089706032, 0.007330763123826495, - 0.016022890367311193, 0.007330763123826495, 0.07023899089706032, -0.03346768844048936, - -0.06351635451036074, -0.05003801442765281, 0.07086545895481336, -0.020146500453061336}, - {0.0, -0.07513090216687965, 0.023854392878864396, -0.018752997939582024, -0.035626994964481226, - -0.07808216015046443, 0.036579082654843526, -0.10605270957897009, 0.0, 0.10605270957897009, - -0.036579082654843526, 0.07808216015046443, 0.035626994964481226, 0.018752997939582024, - -0.023854392878864396, 0.07513090216687965}}, - {{0.5247726717078615, 0.16295052246714098, -0.04560058213722554, -0.007228400216738096, - -0.08929769514117739, -0.007228400216738096, -0.04560058213722554, 0.16295052246714098}, - {0.0, 0.04148190873050992, -0.09855147775337295, 0.04068590273394563, 0.0, -0.04068590273394563, - 0.09855147775337295, -0.04148190873050992}}, - {{0.70499152784401, -0.16991889201466837, -0.20729882500410457, -0.16991889201466837}, - {0.0, -0.003303929642855835, 0.0, 0.003303929642855835}}, - {{0.31511419714426747, -0.09487924242021828, 0.11085303916164066, -0.09487924242021828}, - {0.0, -0.08655345247423127, 0.0, 0.08655345247423127}}, - {{0.527459017500086, 0.0053852573114735736, -0.16311343231991685, 0.0053852573114735736}, - {0.0, -0.1326118886568088, 0.0, 0.1326118886568088}}, - {{0.6102066942477578, -0.07202687894147122, -0.07873717780750517, -0.07202687894147122}, - {0.0, 0.15390946832427843, 0.0, -0.15390946832427843}}, - {{0.5522827825662304, 0.02247888349994264, 0.06894003068860452, 0.054250799183907905, -0.04649970907457038, - -0.025283389794838417, -0.02085901328343852, 0.07420721118594584, -0.011511651888006347, - 0.07420721118594584, -0.02085901328343852, -0.025283389794838417, -0.04649970907457038, - 0.054250799183907905, 0.06894003068860452, 0.02247888349994264}, - {0.0, 0.025696394507123577, -0.06942446748581371, 0.0016937241252907506, -0.10089121574342319, - 0.029974458022771634, 0.08492094910243575, -0.04639575646850304, 0.0, 0.04639575646850304, - -0.08492094910243575, -0.029974458022771634, 0.10089121574342319, -0.0016937241252907506, - 0.06942446748581371, -0.025696394507123577}}, - {{0.43415755465103323, -0.034896028361847214, -0.16002273910462933, -0.018817250009288352, - 0.1549843023935208, -0.018817250009288352, -0.16002273910462933, -0.034896028361847214}, - {0.0, 0.010730391426110649, 0.0701624828082627, 0.05047212452488589, 0.0, -0.05047212452488589, - -0.0701624828082627, -0.010730391426110649}}, - {{0.5562443791201288, 0.26587132099065325}, {0.0, 0.0}}, - {{0.2888899359571893, -0.1524121635077224}, {0.0, 0.0}}, - {{0.34577969092790256, 0.08022313425852307}, {0.0, 0.0}}, - {{0.4526769212546825, 0.19993603476671215, -0.03627467206261856, 0.19993603476671215}, - {0.0, 0.062078206122130886, 0.0, -0.062078206122130886}}, - {{0.6608149891546227, -0.3367713117114448}, {0.0, 0.0}}, - {{0.6300937093064057, 0.15833176475717037, -0.01800611030695498, 0.15833176475717037}, - {0.0, 0.1702159524768959, 0.0, -0.1702159524768959}}, - {{0.5904431377443209, 0.07954608309180142, -0.014921649796201489, 0.0005901097605300332, - -0.08174006039371018, 0.0005901097605300332, -0.014921649796201489, 0.07954608309180142}, - {0.0, 0.057018991161973565, -0.07891646065786176, 0.11081509003436779, 0.0, -0.11081509003436779, - 0.07891646065786176, -0.057018991161973565}}, - {{0.619036005447406, 0.36113904473552993}, {0.0, 0.0}}, - {{0.40575593293484286, -0.17603455720575084}, {0.0, 0.0}}, - {{0.6326754919239695, 0.1591418422174358, -0.22702101194404703, 0.1591418422174358}, - {0.0, -0.04548011120298867, 0.0, 0.04548011120298867}}, - {{0.4784014512253625, 0.011293452743340458, -0.056787040374204935, -0.023693104723780656, - 0.10951072764725725, 0.07974560959080311, -0.07394839942767145, 0.02677419921741667, - -0.010045968526969012, 0.02677419921741667, -0.07394839942767145, 0.07974560959080311, - 0.10951072764725725, -0.023693104723780656, -0.056787040374204935, 0.011293452743340458}, - {0.0, 0.07201888847998587, 0.03497215168058931, 0.05943123839853866, 0.05285004163673773, - -0.01085420783312048, 0.02035937888991158, 0.008553256069694384, 0.0, -0.008553256069694384, - -0.02035937888991158, 0.01085420783312048, -0.05285004163673773, -0.05943123839853866, - -0.03497215168058931, -0.07201888847998587}}, - {{0.4463826700338968, -0.06249810243977311}, {0.0, 0.0}}, - {{0.6138748297032897, 0.08206729827609008, -0.04196993291849642, 0.06274035723249816, -0.06976035181410603, - -0.05171556292590567, 0.00959458517750721, 0.06689841475792666, -0.03127350909406679, - 0.06689841475792666, 0.00959458517750721, -0.05171556292590567, -0.06976035181410603, - 0.06274035723249816, -0.04196993291849642, 0.08206729827609008}, - {0.0, 0.08138486461332381, 0.04127830896039093, -0.10717873814123505, -0.008051188471232124, - -0.02059807337669975, 0.056012288708000416, -0.0144978819539354, 0.0, 0.0144978819539354, - -0.056012288708000416, 0.02059807337669975, 0.008051188471232124, 0.10717873814123505, - -0.04127830896039093, -0.08138486461332381}}, - {{0.5212439749642962, 0.10242981444618521, 0.029577559990000446, 0.10242981444618521}, - {0.0, 0.1488184794663292, 0.0, -0.1488184794663292}}, - {{0.6297051883876085, -0.08399204761426496, -0.32076261758394786, -0.08399204761426496}, - {0.0, -0.007528757590112262, 0.0, 0.007528757590112262}}, - {{0.48407674552921653, -0.008227964517310823, 0.0213027140552919, 0.023595673719326214, - 0.005310690802590445, -0.06652446503281143, 0.08160932212853239, -0.06891582485441083, - 0.052892945583508655, -0.06891582485441083, 0.08160932212853239, -0.06652446503281143, - 0.005310690802590445, 0.023595673719326214, 0.0213027140552919, -0.008227964517310823}, - {0.0, -0.007093439306719466, -0.05389072430458301, -0.014648934153181905, -0.029539361540359133, - 0.028236671825160584, 0.07234447177670174, -0.03961094803635662, 0.0, 0.03961094803635662, - -0.07234447177670174, -0.028236671825160584, 0.029539361540359133, 0.014648934153181905, - 0.05389072430458301, 0.007093439306719466}}, - {{0.40840858719165507, -0.00019587417138387797, 0.01785707194350384, -0.03945144723928065, - -0.05169950516677427, 0.06413973151079717, -0.1021740546918613, -0.03057286834254297, - 0.009965374816382294, -0.03057286834254297, -0.1021740546918613, 0.06413973151079717, - -0.05169950516677427, -0.03945144723928065, 0.01785707194350384, -0.00019587417138387797}, - {0.0, -0.05557309041453939, -0.13647515970329846, 0.09642410678380406, -0.027207097480659137, - -0.007933514822127903, 0.030346016285416798, 0.04319845964314343, 0.0, -0.04319845964314343, - -0.030346016285416798, 0.007933514822127903, 0.027207097480659137, -0.09642410678380406, - 0.13647515970329846, 0.05557309041453939}}, - {{0.6137287510109087, 0.019157896750848162, -0.05426698771160002, -0.107174755578878, 0.04224619970375573, - 0.039969043468570706, 0.004338296440140308, 0.06694608222595438, 0.018542615219732883, - 0.06694608222595438, 0.004338296440140308, 0.039969043468570706, 0.04224619970375573, - -0.107174755578878, -0.05426698771160002, 0.019157896750848162}, - {0.0, -0.06481804503251337, 0.01040922623102531, 0.03115577981125027, -0.03966726556078537, - 0.07314271886708576, 0.05386721014638309, -0.021237196104759097, 0.0, 0.021237196104759097, - -0.05386721014638309, -0.07314271886708576, 0.03966726556078537, -0.03115577981125027, - -0.01040922623102531, 0.06481804503251337}}, - {{0.5353487676283651, -0.0948504913692811, 0.04684477769694442, -0.013771271402122677, -0.0203628406916192, - 0.02514842443382726, 0.08972440209952712, 0.0023200127650800234, 0.039423196857370835, - 0.0023200127650800234, 0.08972440209952712, 0.02514842443382726, -0.0203628406916192, - -0.013771271402122677, 0.04684477769694442, -0.0948504913692811}, - {0.0, 0.02928471698851646, 0.005673137221436961, 0.0113450994077995, 0.07386064700676326, - -0.0921306795918339, -0.07150532757161823, -0.08177764531839596, 0.0, 0.08177764531839596, - 0.07150532757161823, 0.0921306795918339, -0.07386064700676326, -0.0113450994077995, - -0.005673137221436961, -0.02928471698851646}}, - {{0.6135828926485822, 0.12164819021773775, -0.037061355755697134, 0.09554922064593595, - 0.0014673678992679906, 0.09554922064593595, -0.037061355755697134, 0.12164819021773775}, - {0.0, -0.02511081481753711, -0.00724662177834573, 0.06830911674790396, 0.0, -0.06830911674790396, - 0.00724662177834573, 0.02511081481753711}}, - {{0.4992818447040142, -0.026335242639876785, -0.014162971662674932, -0.009204839208816325, - -0.0037880075406994396, -0.039667809092817426, 0.10573273706798308, 0.009221844764489362, - 0.10758448782129032, 0.009221844764489362, 0.10573273706798308, -0.039667809092817426, - -0.0037880075406994396, -0.009204839208816325, -0.014162971662674932, -0.026335242639876785}, - {0.0, -0.09052573027874725, 0.019800562825848633, 0.037129649810409254, -0.02593987015036732, - 0.010443783508403973, 0.04409059421606548, 0.019083289622832046, 0.0, -0.019083289622832046, - -0.04409059421606548, -0.010443783508403973, 0.02593987015036732, -0.037129649810409254, - -0.019800562825848633, 0.09052573027874725}}, - {{0.3477064910489969, 0.069543188579193}, {0.0, 0.0}}, - {{0.4271366217878908, -0.07189292138917516, 0.08837768784741297, -0.028596528627530097, - -0.002667519527527973, -0.028596528627530097, 0.08837768784741297, -0.07189292138917516}, - {0.0, 0.05875422493751868, -0.0034780760585043646, 0.08494469507363896, 0.0, -0.08494469507363896, - 0.0034780760585043646, -0.05875422493751868}}, - {{0.4073432727204618, 0.014805197956703195, 0.05793821643397497, 0.10942961383389802, 0.0070449629545399906, - 0.04114695896896992, -0.08622162389229589, -0.05000037718074825, 0.05048330638107701, - -0.05000037718074825, -0.08622162389229589, 0.04114695896896992, 0.0070449629545399906, - 0.10942961383389802, 0.05793821643397497, 0.014805197956703195}, - {0.0, -0.008842335656083382, -0.032743438452449085, -0.046196276979382764, -0.04312784975457762, - 0.005801406633435733, 0.019074670841659862, -0.07709285356659565, 0.0, 0.07709285356659565, - -0.019074670841659862, -0.005801406633435733, 0.04312784975457762, 0.046196276979382764, - 0.032743438452449085, 0.008842335656083382}}, - {{0.5568364329881461, -0.17902159944745377, -0.07443901563721851, 0.031112786815826174, 0.02768522421570796, - 0.031112786815826174, -0.07443901563721851, -0.17902159944745377}, - {0.0, -0.009434766955953945, -0.04937579307025532, 0.0011667664626842864, 0.0, -0.0011667664626842864, - 0.04937579307025532, 0.009434766955953945}}, - {{0.4062976724756584, 0.013942954902943575, 0.0013568203618884583, -0.07879442082783945, - 0.008667802900890809, -0.05295232243525326, 0.0693361048873288, -0.027851553801721537, - -0.047959823332604595, -0.027851553801721537, 0.0693361048873288, -0.05295232243525326, - 0.008667802900890809, -0.07879442082783945, 0.0013568203618884583, 0.013942954902943575}, - {0.0, 0.10338853801320178, 0.02821697248022432, -0.03906633642088774, 0.05804692097388578, - -0.018017802505488005, -0.008342126196425707, 0.04659679046117832, 0.0, -0.04659679046117832, - 0.008342126196425707, 0.018017802505488005, -0.05804692097388578, 0.03906633642088774, - -0.02821697248022432, -0.10338853801320178}}, - {{0.4680495376375846, 0.019259577430926572, -0.17520944528364238, 0.019259577430926572}, - {0.0, -0.05331435730345738, 0.0, 0.05331435730345738}}, - {{0.5548832978731156, -0.05169341469959422, -0.0363889640149128, 0.14462091688563883, -0.054529502590706436, - 0.14462091688563883, -0.0363889640149128, -0.05169341469959422}, - {0.0, -0.07979294428422681, -0.081075443263044, 0.028480571060195102, 0.0, -0.028480571060195102, - 0.081075443263044, 0.07979294428422681}}, - {{0.44109835329327374, -0.059306377579101247, 0.04042931314500495, -0.12799388287942493, - 0.03703877781680284, -0.12799388287942493, 0.04042931314500495, -0.059306377579101247}, - {0.0, -0.0021153720251691766, 0.09052343268872248, -0.060414554994616315, 0.0, 0.060414554994616315, - -0.09052343268872248, 0.0021153720251691766}}, - {{0.39681990192060773, 0.26969050570068337}, {0.0, 0.0}}, - {{0.4911246395112836, -0.4126437576663877}, {0.0, 0.0}}, - {{0.4502949810735565, 0.022355638075107313, 0.020844343363956436, -0.04704854116453177, - -0.14641617259413728, -0.04704854116453177, 0.020844343363956436, 0.022355638075107313}, - {0.0, -0.04690643540201719, -0.044857271403596466, 0.08203998884768524, 0.0, -0.08203998884768524, - 0.044857271403596466, 0.04690643540201719}}, - {{0.5545389266369071, -0.4011999281193247}, {0.0, 0.0}}, - {{0.5487871437860694, -0.10918772610680917, -0.0204437151263345, -0.07241224402076372, 0.10020675167419157, - -0.07241224402076372, -0.0204437151263345, -0.10918772610680917}, - {0.0, 0.1369624550245931, 0.008569176441236517, 0.11127247059684721, 0.0, -0.11127247059684721, - -0.008569176441236517, -0.1369624550245931}}, - {{0.525689684343914, 0.04293325970025039, -0.030179739992632723, -0.05038212963216292, 0.02307239166039156, - -0.0161186370831989, 0.0303121987961356, -0.029347700828293622, -0.02990503020774543, - -0.029347700828293622, 0.0303121987961356, -0.0161186370831989, 0.02307239166039156, - -0.05038212963216292, -0.030179739992632723, 0.04293325970025039}, - {0.0, 0.031650959576365614, -0.05249532649964127, -0.06330481947587874, -0.03390781494360795, - 0.07112408901498432, 0.04651605442534566, -0.05113269678961534, 0.0, 0.05113269678961534, - -0.04651605442534566, -0.07112408901498432, 0.03390781494360795, 0.06330481947587874, - 0.05249532649964127, -0.031650959576365614}}, - {{0.45846292591343774, 0.057728148175511224, 0.09986385987502491, 0.057728148175511224}, - {0.0, -0.1368499557560143, 0.0, 0.1368499557560143}}, - {{0.524882891146362, 0.008511396928025477, 0.014781289867711371, 0.009887920266107053, 0.006771504255369168, - 0.039851202083645135, -0.018192657733470778, -0.029900823076734365, -0.15309835748275913, - -0.029900823076734365, -0.018192657733470778, 0.039851202083645135, 0.006771504255369168, - 0.009887920266107053, 0.014781289867711371, 0.008511396928025477}, - {0.0, 0.03486331238468826, -0.04200452046561043, -0.029916623097555238, 0.05799517143022398, - -0.04274944723179936, -0.03641639881897832, -0.08556206824813507, 0.0, 0.08556206824813507, - 0.03641639881897832, 0.04274944723179936, -0.05799517143022398, 0.029916623097555238, - 0.04200452046561043, -0.03486331238468826}}, - {{0.1396569380332171, 0.0031113415767125563}, {0.0, 0.0}}, - {{0.5381371205897701, 0.014951746342371994, -0.022742211846719845, -0.06834015662204383, -0.281379724170303, - -0.06834015662204383, -0.022742211846719845, 0.014951746342371994}, - {0.0, -0.043512210721145014, 0.010498527860726914, -0.011177423876693775, 0.0, 0.011177423876693775, - -0.010498527860726914, 0.043512210721145014}}, - {{0.458432915386259, -0.027736276833153158, -0.007691662901534085, -0.001584200240078635, - 0.17753752526665573, -0.001584200240078635, -0.007691662901534085, -0.027736276833153158}, - {0.0, 0.009796688554373531, -0.04673774811126945, -0.07665894627807641, 0.0, 0.07665894627807641, - 0.04673774811126945, -0.009796688554373531}}, - {{0.476464338512819, 0.010629551641227537, 0.03935742410033851, -0.01969212546385932, 0.06487406336960619, - -0.0647718460563506, -0.02137726920635457, 0.03587991384427056, 0.02662510901480497, - 0.03587991384427056, -0.02137726920635457, -0.0647718460563506, 0.06487406336960619, - -0.01969212546385932, 0.03935742410033851, 0.010629551641227537}, - {0.0, 0.030136326600295898, 0.06420857116384811, 0.09222343873200076, 0.0072004835283467905, - 0.08643721045556424, -0.05765328972900675, -0.039739841791084904, 0.0, 0.039739841791084904, - 0.05765328972900675, -0.08643721045556424, -0.0072004835283467905, -0.09222343873200076, - -0.06420857116384811, -0.030136326600295898}}, - {{0.5842109110412117, 0.06755119677610749, 0.10661914181249668, 0.06755119677610749}, - {0.0, 0.2299710231813176, 0.0, -0.2299710231813176}}}; - - for(int i = 0; i < inputData.length; i++) { - double[] re = inputData[i]; // Real part of input - double[] im = new double[re.length]; // Imaginary part of input (all zeros for real IFFT) - - double[][] expected = outputData[i]; // Expected output - - ifft(re, im, 1, re.length); // Perform IFFT - - double[][] actual = new double[][] {re, im}; - // Validate the IFFT results - validateFftResults(expected, actual, i + 1); - } - } - - @Test - public void testIfftWithComplexNumpyData() { - - // Define the input data array (complex IFFT inputs) - double[][] inputData = { - {0.17768499045697306, 0.3405035491673728, 0.9272906450602005, 0.28247504052271843, 0.42775517613102865, - 0.8338783039818357, 0.9813749624557385, 0.47224489612381737, 0.7936831995784907, 0.5584182145651306, - 0.5296113722056018, 0.6687593295928902, 0.9630598447622862, 0.7130539473424196, 0.860081483892192, - 0.8985058305053549}, - {0.5574117454125004, 0.5153534964811869, 0.2218520797445649, 0.6278331124271054, 0.5264697956167971, - 0.9311289722152635, 0.13031360760348354, 0.6303629910772452}, - {0.8630369856754253, 0.9497763465868887, 0.9997691100440745, 0.3303206858594411, 0.9418293674995452, - 0.836702533317602, 0.42360226191376127, 0.28470444389038796, 0.18653359615821063, 0.6164549770372061, - 0.22800778967348623, 0.8045503278555484, 0.7141418111594348, 0.8898346433405011, 0.5648709181981051, - 0.7063388262940075, 0.33464462295280606, 0.5338979798606405, 0.3604790551977437, 0.4551044306632025, - 0.023102489774489032, 0.5939484248343428, 0.601633008560747, 0.5056641001742173, 0.4945185946051195, - 0.3632887663114238, 0.09711935047598264, 0.8610061039014398, 0.1695426000726007, 0.5420844999569931, - 0.22423519306858575, 0.8138053316534443}, - {0.2668982957216445, 0.6518643198476277, 0.38293047888504705, 0.5276109887708621, 0.800891602234696, - 0.665489651348857, 0.5102919050998651, 0.03294208149274114}, - {0.8673593019099284, 0.5449051089951431, 0.35927077618823344, 0.9657806185363754}, - {0.3275052635434117, 0.13324953722224242, 0.9813054584386846, 0.41940627067946346, 0.5854578390799309, - 0.9370081210453547, 0.4965403182131879, 0.40441779810528955, 0.5902823763509569, 0.2816096727771966, - 0.9187065056993756, 0.01750173716395509, 0.9430939103428495, 0.524796393731078, 0.6522622245916397, - 0.7191101906183052, 0.924218305299526, 0.41557703109579636, 0.27205034406713446, 0.9913812499805844, - 0.8474192033613039, 0.7090188824891868, 0.6353901388073759, 0.9455732252453851, 0.5575374113914232, - 0.8165674671938592, 0.9677824877594866, 0.4330735440317255, 0.9448510262521692, 0.4022170497451182, - 0.6565890617824428, 0.3310258105072468}, - {0.7856782800988511, 0.6679548733836229, 0.143535331535097, 0.7943042164876586}, - {0.04873326306922765, 0.15565317347954144, 0.25192661757985957, 0.4991677708509388, 0.3066370428993622, - 0.16093416033095542, 0.7306599702801884, 0.3010950973671792, 0.8167182072302016, 0.35620787574024504, - 0.08043055169163749, 0.3186418802351839, 0.20232598085979203, 0.9823046248429211, 0.1332517204586674, - 0.6984885530480028, 0.2197570335512251, 0.3823643227597068, 0.2993821619426701, 0.7890734732591685, - 0.9150357080758154, 0.814073309530792, 0.6466925662323889, 0.21367632233614853, 0.9317957911629086, - 0.902898345030541, 0.6272897679127241, 0.5146060996086259, 0.47521808342344796, 0.11893945126711991, - 0.8681215624426699, 0.8562757163749677}, - {0.2813501281398796, 0.423403543513415, 0.06307283742785219, 0.1410750085202912, 0.4171503325036342, - 0.4946182389939746, 0.6531869179899719, 0.10773798427753578}, - {0.3042057982656987, 0.36010750174274897, 0.9210922929503627, 0.9192485611716222, 0.5144480274153918, - 0.5814672279115486, 0.6011866421864879, 0.7012821521290435}, - {0.9995986452888619, 0.18328112493260273, 0.38935455191203494, 0.3509391406504685, 0.3263088507735412, - 0.9648828416159605, 0.8423623129564872, 0.3871067276945551, 0.45097017756014, 0.5159198092355896, - 0.954050553063797, 0.5014833734710475, 0.7976092097295954, 0.43190395198337117, 0.12323995739627724, - 0.04493903174415104}, - {0.07843773812596688, 0.7221954247862206, 0.5131226646525481, 0.9489126584469372, 0.47532447735279626, - 0.5050483053898411, 0.11285163895281858, 0.6442680036899163, 0.6943999719718427, 0.11854357006570826, - 0.9433634243731835, 0.8471613997721141, 0.9645297215939213, 0.9946433175329309, 0.9445985285947038, - 0.07499800505216225}, - {0.025606890585376463, 0.12419720934757339, 0.6863742021872193, 0.1591793804631091, 0.20073179883607617, - 0.9163422318494329, 0.8255291764015206, 0.24839917554927815, 0.9017306407233789, 0.011143738439888695, - 0.9499466038149739, 0.23839793578917512, 0.7530603927443468, 0.7221433441575709, 0.9882231809584526, - 0.010882536537176746, 0.6372138133730175, 0.9850167826732869, 0.6096297429504447, 0.6421948734889896, - 0.4673898464886783, 0.8286049778392416, 0.7412286660738077, 0.7568564633736149, 0.512257077801464, - 0.7639553542893256, 0.019409959095512463, 0.44888036476478865, 0.5561388696313153, 0.4010303093814831, - 0.3519885049606841, 0.36180934217779015}, - {0.42957497934079614, 0.3494574872550452, 0.28432737366460115, 0.6512952245877861}, - {0.9004930907404661, 0.24177853554090678, 0.2965518122533567, 0.15880453298324915, 0.808380071501632, - 0.534445489902122, 0.6553342025721992, 0.3775806858178651}, - {0.05039928002703009, 0.6153252657122776, 0.18969212975794214, 0.6401253643448036, 0.6169843888554394, - 0.8312875187886041, 0.9490105226327502, 0.7245238257008109}, - {0.05875630673478127, 0.02409603626667045, 0.8073637232586641, 0.5978959407049632, 0.673078922215489, - 0.5196326131440167, 0.4738950710992147, 0.032500830859891416, 0.8810220711016585, 0.12909173283303255, - 0.9213328273665816, 0.8090874942954726, 0.9354909447748617, 0.882576819693844, 0.04917162075490544, - 0.49253892564640533, 0.3898160399036703, 0.13157304806755854, 0.6997084267185423, 0.23507481777217032, - 0.07693269358017651, 0.41923240484513036, 0.7701296835014443, 0.9230307316770667, 0.44276343787221584, - 0.4556723674991543, 0.0005269550147345425, 0.9469237088461168, 0.43039381632701756, 0.9818691560857561, - 0.0032385749365969607, 0.38460732810872156}, - {0.07177251361521142, 0.597972082172478, 0.6226088866570731, 0.7447140422117096, 0.03608317332120958, - 0.9403392994904969, 0.4214767886988283, 0.5400819129156715}, - {0.7886913760270524, 0.2811741704130385, 0.5337400451604049, 0.8427268522792017, 0.43468289560698725, - 0.4474995478449879, 0.4384795264165595, 0.9811467374329375, 0.07392345242810194, 0.1727386149139265, - 0.9717684346647587, 0.9363308310478798, 0.5655367186251736, 0.7638950609861564, 0.7938449219818952, - 0.1853731675532243}, - {0.8599654952652722, 0.9067031377676662, 0.9192926722382764, 0.7337945168999715, 0.13609124919880633, - 0.9797115711637998, 0.3065888944026701, 0.1660206808739042, 0.27230041646280134, 0.3066077606192541, - 0.2953550472086268, 0.921215997827601, 0.9959872895290139, 0.7446027654086131, 0.32904544352394216, - 0.3278728444599871}, - {0.7347977442629331, 0.2823105544640442, 0.06972698993685245, 0.024892539689898574, 0.8377112725721335, - 0.795264231632406, 0.9831660948336206, 0.6726586022412739, 0.23734748257972293, 0.27596016754533936, - 0.960514274353782, 0.8802020889934468, 0.9420024447650903, 0.32617198465449837, 0.26334781459438883, - 0.9708661080259163}, - {0.9849998857013367, 0.9294648448342805, 0.6941639825602122, 0.03962873250027643, 0.34876857441906006, - 0.7102331264346626, 0.009923227971004533, 0.004291243075037587, 0.42771821843373103, - 0.48228770307558944, 0.29210057013301804, 0.10659978052559116, 0.9511736784606385, 0.6471401175821299, - 0.08000089078354955, 0.1422609026335776}, - {0.2735420881837255, 0.1525514198463197, 0.46667337710664325, 0.8430422042800253, 0.662646859557516, - 0.4273484753734381, 0.9350218443876921, 0.14772485980464511, 0.35831465507175264, 0.12425253795744928, - 0.700261060470252, 0.7134654616253058, 0.628788146059543, 0.9719792883450641, 0.06762978640988282, - 0.23452033520839022}, - {0.38660992002326133, 0.8543958197176149, 0.2379775259300757, 0.9160890057490731, 0.3122712783494066, - 0.7118595870944922, 0.33010723519532603, 0.09124265325192515}, - {0.46464059988353745, 0.4182842589733563, 0.6454742040446713, 0.6751443185405566, 0.33610656116635307, - 0.47217147047337293, 0.5013136121720635, 0.8598453024444461}, - {0.29252431692420444, 0.5445036774354164, 0.22890028277878705, 0.7981586474384571}, - {0.45120803219853756, 0.005439454681410383, 0.29384818661042156, 0.5224946681120767}, - {0.6258267454453438, 0.9485563046471146, 0.14234788814134525, 0.6706414835656253}, - {0.4429692727609613, 0.9816003222145677, 0.9479665512387505, 0.13103087145529047, 0.9784096110774524, - 0.5867703787286896, 0.7145487664819216, 0.5891205516574893, 0.035029622864427123, 0.5995473632960368, - 0.9423034982217162, 0.53901575498877, 0.8050200304388183, 0.3174779164324115, 0.7732143797781064, - 0.8251941778980239, 0.7277950900374809, 0.7736491045301819, 0.710706248482189, 0.8498095803008198, - 0.8226903167987375, 0.2884563905752452, 0.9797919189308799, 0.9737107836254583, 0.8415948491227313, - 0.41465219194912595, 0.4183035359012892, 0.2709684010948765, 0.6950743419687081, 0.9464476910783752, - 0.37199873428524965, 0.17782034671421165}, - {0.7864927572636758, 0.2418789922428023, 0.017035942650692304, 0.3238822138709484, 0.2964123622659881, - 0.8231199960632809, 0.22959849533167642, 0.5128280021192582, 0.826162071747244, 0.6149754443882899, - 0.05850324347630931, 0.01432072231589221, 0.40266236651645326, 0.9357647399130745, 0.7675928523784253, - 0.3251647144191495}, - {0.6712349879452624, 0.8768633991685569, 0.9333296436072808, 0.9719874832801729, 0.9022668072799137, - 0.8633413571017937, 0.816766840903252, 0.6261466465055312, 0.043032952718658035, 0.5274833759436072, - 0.5807849109767159, 0.5534228768710968, 0.6603172096390849, 0.26813850011980267, 0.23236379162038068, - 0.8606794281184019, 0.31203203870718943, 0.8943901140697472, 0.5400791160983084, 0.4529979440888534, - 0.15166416529920868, 0.5656941220474122, 0.272902982304166, 0.009785280483843528, 0.36102057879912275, - 0.2307220210264832, 0.5668194194053567, 0.2515551306244589, 0.9269562373834143, 0.4060242417117247, - 0.09588656778378668, 0.4471162777527842}, - {0.0420959635135838, 0.10953891340341015, 0.02228427097568375, 0.3299764529754966}, - {0.9669732816839842, 0.13899760644982895, 0.4414297951394093, 0.8240552945282527, 0.8371972666712111, - 0.10373354060386875, 0.20045983989759142, 0.023505541760897364}, - {0.5200251176535067, 0.4039794590721878, 0.7590485264572412, 0.6631651631903216, 0.3989003558798062, - 0.693767248255806, 0.659729341908985, 0.20088033104573177, 0.11011106048831465, 0.7914612301673087, - 0.10174794142966948, 0.06490845280803292, 0.6044074726666192, 0.30040990308697135, 0.6401444966694853, - 0.5159688446015483}, - {0.9567414718212607, 0.10348104171557015, 0.25219429120273673, 0.2705602567028519, 0.579440552110363, - 0.11726486785687817, 0.6130270953780019, 0.9521949368720519}, - {0.42950635425979655, 0.16518745944643443, 0.13458413628474974, 0.9285861639383158, 0.5740206966788683, - 0.9746846403184233, 0.23986276321155475, 0.7791645261174039, 0.6942989577222445, 0.10302437381407292, - 0.17649726059158877, 0.44259701923012407, 0.12642636197760193, 0.018563874910113687, 0.6702218490390617, - 0.8395756519279107}, - {0.9837398631005024, 0.8970651242018116, 0.45841877916367335, 0.9602393019645956}, - {0.8525631081163496, 0.9644669729172649, 0.9583040327078675, 0.08720862736937618, 0.38057991297468763, - 0.9172629043363064, 0.21015703777716566, 0.398590750888154}, - {0.9553448577457966, 0.3808446826563595, 0.2234692691701734, 0.6931884449795152, 0.4776102415216825, - 0.6379769378552478, 0.18197920456077177, 0.5495763216802875}, - {0.2246136780598329, 0.05767747155215186, 0.34443655686657215, 0.23626373941110268, 0.2094919046714232, - 0.33148396737427854, 0.6989592823292484, 0.3075750586735163, 0.30999602181149644, 0.9444805419325929, - 0.5350272394699568, 0.10259125359638843, 0.92251474463751, 0.5766628546939399, 0.008985724914360338, - 0.6161053035242512, 0.6803430663571829, 0.19806467700559893, 0.8496720223691024, 0.797725223954955, - 0.42977026069183044, 0.2859623992406095, 0.582898925325729, 0.5438471000543933, 0.5567522706171357, - 0.9287397533406584, 0.5315422813533052, 0.7355446639294867, 0.8141949453122773, 0.5926503762438441, - 0.5887798047494944, 0.4067989362143932}, - {0.2575640558985337, 0.49455586531763174, 0.23116287667094992, 0.8184204285887713, 0.5791911859886936, - 0.5561610722618936, 0.6207159235834669, 0.47523356819461293, 0.39187688553339817, 0.11686459005876793, - 0.29265769310006484, 0.46897734527879675, 0.7074845790562786, 0.1194478327696622, 0.7293916292770758, - 0.6444884178736836, 0.9230055868902729, 0.2842944321592319, 0.2891647472388498, 0.9349759803763106, - 0.7441865370192212, 0.19074195964463103, 0.8909968206481071, 0.38663989410484945, 0.7840420007487027, - 0.09858775872012648, 0.6036847723227681, 0.883894562861853, 0.017355209220390688, 0.17825703513265356, - 0.8838845828873518, 0.44563802678121645}, - {0.6254574882890985, 0.3396845506691285, 0.5980555987748056, 0.8747864117436839, 0.2157196222997838, - 0.3556636249319295, 0.053806813459211233, 0.2346442931802648, 0.4796148685005919, 0.24112694504168541, - 0.7790663644847666, 0.2409912290072156, 0.22051206627906483, 0.34180702048144307, 0.4699843195392497, - 0.48928572788799174}, - {0.7474292432892988, 0.16858497841711506, 0.6628545680181327, 0.6366739239935207}, - {0.6545412531900626, 0.9031939852903598, 0.7828131393210792, 0.07194999459094109, 0.3202994846650421, - 0.5967882845856519, 0.07297696421889999, 0.32558559441289847}, - {0.9028548797400592, 0.6086914534870387, 0.8503682936674709, 0.08381072759650299, 0.2694175337129405, - 0.7154539330685599, 0.966551261889283, 0.6532594270377007}, - {0.7740125823482259, 0.0673750838824213, 0.12989631420367154, 0.0877755681264033, 0.3412272314782776, - 0.6210077405051666, 0.942184839928994, 0.6923106498188738, 0.3584424101818512, 0.994329394089604, - 0.49811988062498436, 0.20711949995802992, 0.8099216616141034, 0.21757791554483763, 0.27855614423300834, - 0.9116369981465458, 0.966439106063462, 0.804671803881807, 0.7608633527861054, 0.4663260189301722, - 0.9812388750291278, 0.5258364881964462, 0.3721517785904038, 0.9167812394734067, 0.9391534487703991, - 0.6923921715592023, 0.49034089746468823, 0.4070031406140925, 0.7212510294366945, 0.48610987062902467, - 0.9263050142815049, 0.06288898483521521}, - {0.8286058511962618, 0.9479079687392229, 0.7472632147955719, 0.826400598301367}, - {0.19375704405770544, 0.7452524160661296, 0.6989900431000508, 0.5733421837141005, 0.03610007233720314, - 0.629437685501308, 0.10711005132092344, 0.23736617274617278, 0.47058505183620924, 0.9474018039618406, - 0.41380657435989154, 0.2797356104560911, 0.40189141715709087, 0.7095520981699313, 0.5270923628575122, - 0.2552494536444865, 0.780412379694144, 0.19845260273689336, 0.18007580659532352, 0.9951276621564948, - 0.3505004853158996, 0.816271984040408, 0.5073348370147777, 0.7662185431411317, 0.29447235072455025, - 0.32016828363095995, 0.5678785309713512, 0.9866334325870364, 0.011830014044379333, 0.8701512387695588, - 0.11728005917738937, 0.837656114582156}, - {0.3361744018167824, 0.5488182808161421, 0.2935753360366993, 0.36922806727087, 0.37182055302205785, - 0.5528849186874375, 0.8736774384863598, 0.6420927398375746, 0.431418272383705, 0.8664139196917229, - 0.9245370577416686, 0.7188260085556081, 0.2436988596361528, 0.20032224181133174, 0.9188259610602927, - 0.7837351503328618, 0.14429063324740077, 0.802472962121198, 0.38298524453499827, 0.9871655455479117, - 0.796096414779996, 0.7821577119408007, 0.7505212770860117, 0.624075838277507, 0.6798108128393442, - 0.9848602164012445, 0.9567333846583475, 0.5780032613282218, 0.32043447337355746, 0.8090405537644711, - 0.8502388833490756, 0.6349005912038598}, - {0.9179840062514263, 0.7448040392988351, 0.871309600449137, 0.7649258038573322, 0.716100362833065, - 0.9148935664681578, 0.36841176665067554, 0.06931652410814748, 0.7413457445431846, 0.4517734106523168, - 0.2610255176116475, 0.3345233658972291, 0.40717712479269064, 0.2818599520541738, 0.9284748949126728, - 0.21060462735027263, 0.5435696081303083, 0.21541396526874246, 0.048166909822641824, - 0.034400237572296044, 0.8615299527849822, 0.4141675345196427, 0.4333206313477377, 0.06532590436538288, - 0.2901270916499121, 0.7036385858160966, 0.8271996390451494, 0.719691017395964, 0.07851349092069604, - 0.05523162021177064, 0.13163226264668004, 0.6533387611647784}}; - - double[][][] outputData = { - {{0.5554009454874607, 0.017189765873370448, -0.12592905534215526, -0.011757491289470089, - 0.07312549803852456, 0.0029102156297631868, -0.19987730488982902, -0.13337758305069133}, - {0.7481466528055458, -0.08167572695583827, 0.098339013093207, -0.0814324809790821, 0.03846232230409691, - 0.02581048631100489, -0.006576466032461176, 0.052609399032017695}}, - {{0.48061260851633936, 0.008698421132479317, -0.09098069593780675, 0.15908141170148843}, - {0.5545688416281973, 0.07091914301684876, -0.226177140018057, 0.127158950989808}}, - {{0.6462796640314765, 0.1207206107818119, -0.01762005486206572, 0.006306293048971567, 0.06822824850575576, - -0.003497250886612027, -0.03677191329183099, 0.09200845566382201, -0.031055683991221217, - 0.006213039230073512, -0.012146295246651465, -0.07738509259143851, -0.007066788422857184, - 0.08229947582855819, -0.08506188580578779, 0.1115861636834207}, - {0.43587965950398616, 0.01119474204674489, 0.0436034631759588, 0.054078336398618976, - 0.056574744780298086, -0.04895519013390881, 0.03450482991652083, -0.10638546065835308, - -0.14772029516547686, 0.0429305206422676, 0.0658742677386075, -0.03671568243477094, - -0.08928203226755357, 0.01178332361684557, 0.015146971096621827, -0.007867575303600903}}, - {{0.45732602080629536, -0.1871449382548796, -0.1324116335029496, 0.1291288466731783}, - {0.5024038100440398, 0.10371325705289913, 0.15318794362324079, 0.041586591514516325}}, - {{0.7061322054525357, 0.16122709645739264}, {0.6625256973623044, -0.303254921174071}}, - {{0.5582658511001826, -0.014704840584211537, -0.0627157060618794, 0.012456193354444395, - -0.01530010739904733, -0.04788008169393359, -0.03738544216219424, -0.10896799274342381, - 0.128628385932322, 0.07670205169968633, -0.007111511533772388, -0.013410977950846295, - -0.06000928230416999, -0.055453451900711194, -0.045478367624256937, 0.01987054341522309}, - {0.6781420149381103, -0.0500509217664482, -0.05026096269054299, -0.014056755596229473, - 0.06615260263105256, 0.0270935493730691, -0.1417803225644949, 0.10813300914922121, - 0.047587732401997485, -0.01667033621629791, 0.10534782624188156, 0.15598626905541169, - 0.026624136604945267, 0.04188891427097305, 0.009064830782525346, -0.06898328131564806}}, - {{0.726816576741237, 0.05886170335761409}, {0.4689197740113778, -0.3253844424762808}}, - {{0.37769853062274406, -0.06287167118587406, 0.013272760755422566, 0.059798661777511176, - 0.020843863564936352, 0.06255457992886422, -0.020505282720239305, -0.12710747661681882, - -0.056363111364126944, -0.07719436952748313, 0.10480606991456991, -0.014618947965474575, - 0.0014243406910924988, -0.2244391814188424, -0.008451436314684419, -0.00011406707236942537}, - {0.5984499821819326, -0.06302615132947265, -0.07883953880314987, -0.004324658112628804, - -0.003873306839043633, -0.11366765114650743, -0.022871220314031784, -0.13800116659867828, - 0.024461602161048757, 0.04561090297164721, -0.017442397228411198, -0.020345419001976284, - 0.016413376549411607, -0.020849024388695465, 0.05947791464931039, -0.041416211199530026}}, - {{0.2272253794003595, -0.04215074100110286, -0.05501389661649361, 0.15128938635711656}, - {0.41817336844127917, 0.011572987376696542, 0.11699525680552395, -0.12959128011986537}}, - {{0.6261635385326081, -0.12426789261679225, -0.01351449292457746, -0.18417535472553972}, - {0.599596012410618, -0.16146991854999235, -0.04177867760967813, 0.11810061116444429}}, - {{0.555479274478064, -0.13335701393571267, -0.03840134060123515, 0.20612101698012242, 0.08392681575466732, - 0.09397681364766289, 0.061948998399705385, 0.1699040805655877}, - {0.4775145080229961, -0.20516430386037218, 0.07267587187915008, -0.026058009209961, 0.10395296641445628, - 0.005252605556895273, -0.02985365267173483, 0.05265019142871023}}, - {{0.5000201139246306, -0.04800345446956701, -0.0329049463626208, -0.03284470537729227, -0.20508598415359822, - -0.05090945428176026, 0.01485192433096992, -0.06668575548479515}, - {0.6977797423695709, -0.08329928338393976, -0.07437114892036442, 0.10800747144622731, - 0.18894316926384197, 0.11583460240335249, 0.017113084069833556, -0.27560766527667935}}, - {{0.4851180273990343, -0.16126949568571958, -0.06691674009092896, -0.10704718961757465, - -0.14611311380319633, -0.035002563984131076, 0.02014526618951898, -0.05833761506438867, - 0.18128233338238373, -0.03264166614264144, 0.12163209118053736, -0.09121827833544369, - -0.05000481625592715, 0.03207004406365072, -0.08147428234704425, 0.015384889697247134}, - {0.5677253092727153, -0.03729235153062347, -0.039365455693876845, 0.04146785092046644, - 0.09798188935435953, -0.008627080611540536, -0.06552852244983984, 0.10072436882505036, - -0.08081824922584976, -0.03604185729698669, 0.03292928365596532, -0.0122484248012707, - -0.04163904757760628, -0.02488167514502858, 0.10345023825137331, 0.039377537425709944}}, - {{0.3895162332979207, 0.04005874604287546}, {0.4678112991261936, -0.18348392546159248}}, - {{0.39940699287949466, 0.11176911860071312, 0.19911545861741672, 0.1902015206428416}, - {0.5939351124484545, 0.05900496787177262, 0.137922024588461, 0.0175179665929438}}, - {{0.3738855099605134, -0.061514135704676315, -0.2538398050680273, -0.00813228916077971}, - {0.7804515639944012, -0.0892065581024592, 0.002545891749693663, -0.07680650878619621}}, - {{0.5179707425469033, -0.025302819300953475, -0.08988468070186949, -0.15759688083449927, - 0.04986716326586882, 0.016711033222231685, 0.0893262152057811, 0.011837069383857, 0.08204319336636628, - -0.21049563858248832, 0.015427354145726171, -0.07133856817868453, -0.01279403797244072, - 0.10188626425620104, -0.18206676093811552, -0.07683334214910278}, - {0.4557183244222546, -0.10900814441739004, 0.04904230378999857, -0.0010906845210327046, - -0.031895227628587916, -0.037331861262286915, -0.040831099380204676, 0.0030723082051012374, - -0.10402962094045479, 0.09107053711775343, 0.14231804953997862, 0.06072556870526918, - 0.015183021067558186, -0.023570386570056008, -0.0692160119825995, -0.010341036241630921}}, - {{0.509266881164118, -0.23777343990417177, -0.16207618102797572, -0.037644746616759095}, - {0.4844952936065516, -0.13303389385421258, -0.25571531259653263, -0.05966291383459679}}, - {{0.5935176438976462, 0.005419535054208113, 0.05452812784476206, 0.05483345393049244, -0.04461918309489521, - 0.03860170688009229, 0.008260547169506763, 0.07814954434523974}, - {0.5579264002751396, -0.19510727214536616, -0.2776691321309364, 0.01833209516889496, - 0.043341981649842765, 0.09601908528205956, -0.003869164267408187, -0.16505054140412423}}, - {{0.6260210272262958, 0.02432692813809728, -0.003993313239643373, 0.12917970105240434, -0.07053644945003956, - 0.16506423245734803, -0.05346289230457359, 0.04336626138538329}, - {0.5241234456299799, -0.061299666157965914, 0.20381084080210454, -0.03215905928455756, - -0.05095139644888391, 0.03355389235031433, -0.04283903698729301, -0.3019386034408972}}, - {{0.5500660037041453, -0.0756423721419075, 0.2210689971105149, 0.07481755482487502, 0.10628452169723956, - -0.1243776248752409, -0.09116501409436648, 0.07374567803767317}, - {0.6070515456890231, -0.3012803549751432, 0.04193893532024, -0.08007158532442629, -0.006248541615777148, - -0.10324316179539066, -0.05306697572107942, 0.1322676210022765}}, - {{0.4651842021869838, 0.08699362121518653, -0.03136073598867131, 0.10751032697033988, 0.04427971547591952, - 0.019039286768015494, 0.18878104838596632, 0.1045724206875964}, - {0.39116023270347816, 0.031180108596409038, 0.32532105395892885, -0.11704207031957495, - 0.04658810674925612, 0.009016215044166007, -0.07362344496447837, -0.1848819833344538}}, - {{0.4885688910675007, -0.18086781613949704, -0.07671903790273893, 0.14878362006771667, 0.09590215124139359, - -0.07456619521904288, -0.03965753053553456, -0.08790199439607198}, - {0.474901408893455, -0.17244598635312056, -0.023956901826718974, 0.1791658005916335, - -0.036152996890597366, -0.012259503214089251, 0.07875989038950917, -0.12969705651831892}}, - {{0.5987680678550062, -0.11799613493734534, -0.2864743448783378, 0.19231233198393816}, - {0.36137018847278746, -0.019882285719344417, -0.04018093170042117, 0.010964307296384701}}, - {{0.5508858453605304, 0.051710056952484845, 0.004171556603573934, -0.14212685903305175}, - {0.5423592365640588, -0.10551677764322767, -0.1236491498948506, 0.02291325214037246}}, - {{0.4185139971798104, -0.12598968025560597}, {0.5135294651086221, -0.28462918232983503}}, - {{0.22832374343997397, 0.2228842887585636}, {0.4081714273612491, -0.11432324075082756}}, - {{0.7871915250462291, -0.1613647796008854}, {0.40649468585348525, -0.26414679771214}}, - {{0.6380761918458395, -0.022978944297255756, -0.0014396124781275166, -0.02384143615782447, - -0.07921880781102483, -0.06801029020723776, -0.15490186756072524, 0.03355342032660816, - 0.06685652476192966, -0.01586151971323415, -0.13389162215415135, 0.005533156811150963, - -0.060356774511329564, 0.17693167298435386, -0.03612458427971646, 0.11864376520170625}, - {0.6414668453372225, -0.012718518336902778, 0.024705495293850604, -0.07647416157394636, - 0.06296179906251137, 0.11361886055723146, -0.02420694444897769, -0.03841719639006607, - 0.05452753410368566, -0.01239440538295344, 0.032061027655300084, 0.06376752807408094, - 0.012832470978494881, -0.07360848144902933, -0.01965325840198135, -0.020673505041039646}}, - {{0.4039060952260403, 0.17105094720582825, -0.04687300825237843, 0.06312719676982903, -0.07152120584803218, - 0.12874155376912266, 0.2559406786392022, -0.1178795002459361}, - {0.4931432693943547, -0.04258744279908336, 0.07887713934060492, 0.012311293243742596, - 0.020586864135253208, 0.09532173093653501, 0.021804946261635733, 0.1467042712342011}}, - {{0.6492600132374694, 0.07405704598578203, -0.10652150413678427, 0.014145446840794215, -0.0763605684643836, - 0.07600883542245865, -0.03496573571710465, -0.06855537550667139, -0.04424787015115095, - 0.06760348676948996, -0.09178162857206457, -0.025320785111737663, 0.040561414773794824, - 0.13320415864997182, 0.021189849362183932, 0.042958204563214517}, - {0.40535288984911627, 0.17032068199517506, 0.01331219400387537, 0.03853473160782414, - -0.012526554240257781, -0.06187945408577552, -0.036479509907725347, -0.07188532577134454, - -0.0019327516265471578, -0.08139500313648848, -0.0058851768933687165, 0.025922987062677243, - 0.047024671064922474, 0.0211940396143128, -0.072339453496859, -0.06530692733234739}}, - {{0.07581743845849698, -0.03372147494491318}, {0.17613036197559018, -0.15384609099990643}}, - {{0.5928639944503687, 0.11132887192540089, 0.11133754396132797, 0.15144287134688658}, - {0.2912240472333921, -0.012080065326201012, 0.2276045060510091, 0.3304487787130108}}, - {{0.5374369429329483, 0.0124309756250367, -0.1263557788347015, 0.010780749436562376, 0.0469888925419365, - 0.15244935362834236, 0.0013926801264731864, -0.1150986978030912}, - {0.39114492523974376, 0.049146273279162184, 0.02579091351846495, -0.14222691912065577, - -0.027042182426221584, -0.14789058018667428, -0.03263438975452017, -0.0061769800609844105}}, - {{0.39574426536060486, 0.3848693124084244, 0.20872361615139384, -0.03259572209916245}, - {0.5654818630543237, -0.05016643956373015, 0.030751960689858693, 0.03337316792991071}}, - {{0.5281995925319434, -0.013483007703420306, 0.22370807173452606, 0.032600345010997386, -0.183706104923201, - 0.10078556921052059, -0.06643803387393597, -0.19216007772763355}, - {0.3839006686515898, 0.04203486886439816, -0.07423404752770826, -0.0167526930749399, - 0.03296043868103443, 0.07361362334006122, 0.06773560004500725, 0.1850404987428018}}, - {{0.940402493651157, 0.04333736944934541}, {0.7093290405641345, -0.25091026140046113}}, - {{0.7156356852777146, -0.15610326950991757, 0.189797885134394, 0.10323280721415865}, - {0.4766476514940784, 0.2619203051863527, -0.18127917611815178, -0.1767088675875917}}, - {{0.5632118136379611, 0.16086874310016572, 0.026195249820023803, 0.20506905118764587}, - {0.46178567640449736, -0.004178181340561221, -0.1319909533632702, 0.1519936998210166}}, - {{0.4016790839699139, -0.03003822458401474, -0.057149866644219526, -0.011524063301240554, - 0.034856641495165167, 0.13554174060998003, -0.09095916185362438, -0.0018328637444979812, - 0.005074060125136143, -0.03801226191198792, -0.043716698741379005, 0.0030306419969509946, - -0.024955698295149528, 0.007269331103218465, 0.042476489879821895, -0.10712547204424006}, - {0.5952054191724998, -0.07676059511106324, -0.026180524219684337, -0.04836907409057477, - 0.03599606184553125, -0.09752988130462281, -0.018060463584829087, 0.050791615691301736, - 0.034038777924507435, 0.07585998009242355, 0.04676163922757573, 0.16844664234113793, - -0.044975123197931816, -0.04892751473324741, -0.004238118680509565, 0.03828422498466859}}, - {{0.46901212184076757, -0.01535709462655225, -0.03916243436874867, 0.060857712029923704, 0.1225909913945583, - -0.09340926175888574, -0.1289223230381446, -0.010663880940625405, 0.00724348179779008, - -0.060944417834272645, 0.06976206691384879, 0.03620812577362522, -0.11481741841389, - -0.045575265138713114, -0.060986015410215574, 0.061727667678068}, - {0.5337093691722835, 0.04436500074224131, -0.030963373409186423, 0.020253982238142385, - -0.0824519992966499, 0.012010153722473894, 0.15829867840518805, 0.04533676610748462, - 0.1083306629496745, -0.08650086214010269, 0.045865730697915885, 0.026540593157353194, - 0.05755930064433876, 0.03279325594388378, 0.06317542465592338, -0.025317096700691388}}, - {{0.4122273004184882, -0.014566204465011313, 0.04208221121586804, 0.17869124295327457, - -0.038967419712763435, 0.03973015972596076, 0.005246463372848328, 0.0010137347804333452}, - {0.4077985676527511, 0.1686350835680268, -0.12037578482090629, 0.006478097285106572, - 0.07949583704816715, 0.032202813316253534, -0.016855152490183614, -0.0777645930586234}}, - {{0.45800711085320694, 0.2894221324360919}, {0.6497642460058267, 0.013090322012305977}}, - {{0.6031245930981106, -0.09986864407594251, 0.11555260315746024, 0.035732701010434215}, - {0.3289125819706231, 0.2696416277863902, -0.13227435752865208, -0.14598036756331914}}, - {{0.6114313386227679, -0.002426979989567729, 0.2651802480809971, 0.028670273025861864}, - {0.651170538927121, -0.04306325057145169, -0.03318614112600926, -0.3055036135167195}}, - {{0.4957183696678124, -0.10553486119506748, 0.052758247615336755, 0.06020035276185396, - -0.013822765236362097, -0.02389059354934929, -0.08857441873167826, 0.008621444261494117, - 0.02082676340882711, 0.05884859157016205, -0.04918841735798534, 0.1405177719428041, 0.0681786035653371, - 0.10947244481774009, 0.08033111333375087, -0.04045006452645019}, - {0.6574845762838595, -0.031453441409394496, -0.04620430117138156, -0.08424489796710648, - 0.06624180213432235, -0.13120364199595552, -0.024391758054135494, 0.08573068590472949, - 0.11223336151893865, 0.07527943774180709, -0.0024984661992818805, 0.09946105848481208, - 0.06606087488780027, -0.022974547547147776, 0.12387018801680866, 0.02304817543478708}}, - {{0.8882569099677424, -0.059651058771480525}, {0.7868319065484695, -0.03956869175289757}}, - {{0.45166687758041546, -0.0018649641234558647, 0.04104745825547753, -0.04331775004206736, - 0.04599538731492686, -0.03347890915900812, 0.05706572867550946, 0.04407075696339463, - -0.09550030045209212, -0.01294296290526355, -0.028171062627338407, -0.011479435442369852, - -0.126578568096198, -0.1055877835747785, -0.01335447270374341, 0.0261870443942967}, - {0.5375290203239034, -0.02717126518144706, 0.03769657408945279, 0.05903704597317041, - 0.10941228619740563, 0.034836047977018406, 0.11449317534415826, 0.03329000205035401, - -0.18630596238167652, -0.03847833560601357, 0.11119698020303745, 0.10426826809732498, - -0.10133153669488919, 0.06085072384786873, -0.08524817187204464, 0.016337527326520968}}, - {{0.5672530754492042, -0.07621450260422805, 0.04780501932723003, 0.1360648011834046, -0.13636811964603351, - -0.05559164579848517, -0.07319574079493588, -0.022375181723106995, -0.01803709042623941, - 0.012598739926626794, 0.003834329770772228, 0.02279304289413908, -0.06706984366225677, - -0.023519044517253875, 0.05957470708250281, -0.041378144644557704}, - {0.6927367377783716, -0.08658190014174541, -0.044710161680140095, -0.10627851813733222, - -0.08408056627365093, 0.02806037261656114, 0.03681651148127257, -0.0438823647392234, - -0.08259784729478026, -0.0660019535766834, -0.06364239429924656, 0.005461792317948917, - -0.04090024064986589, 0.022673859550358066, -0.0015713160185880162, -0.021211377685855435}}, - {{0.5615331442331852, 0.09973756929788653, -0.043561441091611794, 0.10577901659492642, 0.027355604592400162, - -0.07846639287809637, 0.06769255556633608, 0.11495862363660969, 0.08994548302237715, - -0.029898108867756554, 0.07176626712854703, -0.04707555302652763, 0.016817577757129054, - -0.14296761759104473, 0.03811568418894253, 0.0662515936881235}, - {0.37970420079142386, 0.035427020131707, 0.03758585103249978, 0.09252346890432356, 0.08421133424276199, - 0.0013340546210409143, 0.021541970955435447, 0.03079966148676702, 0.022053247502089618, - -0.05621550573850214, -0.07144813696100247, 0.0005035402070686645, -0.04253374666480074, - 0.16004586961594686, -0.01426637100829721, -0.1376968509881538}}}; - - for(int i = 0; i < inputData.length; i++) { - double[] re = inputData[i]; // Real part of input - double[] im = new double[re.length]; // Imaginary part of input - - double[][] expected = outputData[i]; // Expected output - - ifft(re, im, 1, re.length); // Perform IFFT - - double[][] actual = new double[][] {re, im}; - // Validate the IFFT results - validateComplexIFftResults(expected, actual, i + 1); - } - } - - private void validateComplexIFftResults(double[][] expected, double[][] actual, int lineNumber) { - - int length = expected[0].length; - - for(int i = 0; i < length; i++) { - double realActual = actual[0][i]; - double imagActual = actual[1][i]; - assertEquals("Mismatch in real part at index " + i + " in line " + lineNumber, expected[0][i], realActual, - 1e-9); - assertEquals("Mismatch in imaginary part at index " + i + " in line " + lineNumber, expected[1][i], - imagActual, 1e-9); - } - - if(lineNumber % progressInterval == 0) { - System.out.println("ifft(complex input): Finished processing line " + lineNumber); - } - - } - - // Helper method for asserting equality with a tolerance - private static void assertEquals(String message, double expected, double actual, double tolerance) { - assertTrue(message + " - Expected: " + expected + ", Actual: " + actual, - Math.abs(expected - actual) <= tolerance); - } - - private void assertComplexEquals(String message, double expectedReal, double expectedImag, double actualReal, - double actualImag) { - final double EPSILON = 1e-9; - assertTrue(message + " - Mismatch in real part. Expected: " + expectedReal + ", Actual: " + actualReal, - Math.abs(expectedReal - actualReal) <= EPSILON); - assertTrue(message + " - Mismatch in imaginary part. Expected: " + expectedImag + ", Actual: " + actualImag, - Math.abs(expectedImag - actualImag) <= EPSILON); - } - - double[][] reInputs = {{0.6749989259154331, 0.6278845555308362, 0.995916990652601, 0.511472971081564}, - {0.5131170807843421, 0.21947931088584838, 0.8291802449178103, 0.7771605380836767, 0.8702643857540487, - 0.8710395079045988, 0.38542176916377324, 0.2881994247798211, 0.1928317731008281, 0.26952861717454657, - 0.15144147822174414, 0.41960334787295883, 0.45034112558576034, 0.1583971541867757, 0.957780647411057, - 0.6758004971959929}, - {0.307168369495708, 0.2925436063741176, 0.41391507728373833, 0.3005219437466511, 0.5878469029586312, - 0.12931493281234274, 0.9278465629379351, 0.17121813246774298, 0.4177834301993081, 0.3769141990814494, - 0.6753379435396163, 0.38247139181867484, 0.24735225836867192, 0.31494194715109947, 0.8137890716670311, - 0.20924977998686056}, - {0.12235304422259108, 0.8698130261511119, 0.4337042152439805, 0.6857925372785122, 0.14428854345470843, - 0.33600839167062124, 0.8119488842140201, 0.11798945578653453, 0.010947620425100557, 0.9717416040360395, - 0.12125654222557414, 0.2617819654240723, 0.6486301555911228, 0.7365333531211434, 0.2853381645844496, - 0.245649529407872, 0.6297339098238705, 0.27490823498920813, 0.16976743283098572, 0.6735736955293987, - 0.6966873975788616, 0.03317205897839892, 0.06774514015627431, 0.2345911011238595, 0.056861430818630154, - 0.9458602964130863, 0.809611874188032, 0.2508944819690482, 0.41904987235844393, 0.44527473435246423, - 0.3598767804057533, 0.6738051864030766, 0.04313688787812686, 0.14040411518698392, 0.6279634929162148, - 0.6907852049110121, 0.010900703530978717, 0.22548934759676642, 0.42249315955719224, 0.7566767789254828, - 0.5549511666098619, 0.2132782383128563, 0.13319453700750528, 0.9913307843402898, 0.5918930102432697, - 0.8123119094264839, 0.06669536255452357, 0.1404352024672152, 0.5822461498734745, 0.9507715524385827, - 0.5149268016834162, 0.46487304063142065, 0.14177878291874513, 0.3030540409436946, 0.7390807099554567, - 0.07855298022742563, 0.7267851554463288, 0.3003808861768813, 0.6942310295833771, 0.02659985118413699, - 0.08047625367936184, 0.25129044839422787, 0.21921500696568008, 0.6446997331877551}, - {0.6467394714302301, 0.37939437912607255, 0.24389382952413974, 0.9230517610649195}, - {0.8133403772356219, 0.9685127164754849, 0.9527118851443901, 0.16168777807158385, 0.3059554069336482, - 0.5098875351782225, 0.18776670485231783, 0.9552937947531795, 0.26630009198521765, 0.7607275302116739, - 0.22743564839228736, 0.2579812237254093, 0.16461081160421187, 0.2568798739085394, 0.6664580672719578, - 0.611154187704012}, - {0.27346385127769657, 0.9768706836769414, 0.63435141212605, 0.09267578126266052, 0.6091291796709725, - 0.8003601253351955, 0.21441231066064415, 0.7574882595853318, 0.6175964091894415, 0.026447112091714353, - 0.9838155477830359, 0.2879060542830987, 0.293010252419861, 0.44472456980740893, 0.18371859899106335, - 0.9697152014778653}, - {0.9241946993360354, 0.93609012256163, 0.7262744168234273, 0.8953331805633857, 0.6737803589116659, - 0.12819148236368838, 0.18661306972239466, 0.7389050320153769, 0.7671764150606258, 0.4513549998697196, - 0.8730680721562523, 0.5506214841129061, 0.03430880982544926, 0.7895272245140907, 0.9546984540561432, - 0.42833058697767035, 0.2900777210978328, 0.8562805913655864, 0.5473173303230449, 0.4148313135430701, - 0.24482377481230033, 0.1537057907732975, 0.11035564325990421, 0.5386796809145485, 0.8661412254170333, - 0.8498517303709959, 0.5906863904920534, 0.9078942281308974, 0.7387794781959018, 0.720441512828483, - 0.9561370849174334, 0.3347594847372559, 0.48786193718206394, 0.9919064656347301, 0.4058367062974416, - 0.8130018855584936, 0.7671992644775938, 0.7621070450721142, 0.2968734799496455, 0.08903123300124016, - 0.8438017683491356, 0.9911353232857506, 0.7124852788354706, 0.8772702766537811, 0.34692831949403335, - 0.6506408096815364, 0.22319555392893964, 0.33156033109536054, 0.8234621132297556, 0.9015756069246127, - 0.4512357762048391, 0.12584571471970663, 0.53112509650375, 0.3972399589722395, 0.5149797348786508, - 0.7327411500784157, 0.45127893858601775, 0.016770807338283178, 0.26719900538319863, 0.3129575148836051, - 0.8896414390923291, 0.40604886799160245, 0.11009325815063131, 0.2697706263683922}, - {0.8287813507102393, 0.1908501784393708, 0.2003356406350958, 0.6681197614203025}, - {0.06238193735772257, 0.27323433977164435, 0.040380413052192865, 0.19662508742378038}}; - - double[][] imInputs = {{0.8330832079105173, 0.09857986129294982, 0.6808883894146879, 0.28353782431047303}, - {0.11858471405750115, 0.6670449264234233, 0.7918053251189741, 0.18474324421305366, 0.2267737011892611, - 0.6112631028641866, 0.43732697326158654, 0.024562867771134922, 0.5846651001654705, 0.9231757238526722, - 0.32785888537720953, 0.058059707791723425, 0.888849882806027, 0.34043171288025054, 0.37505905249694726, - 0.4989225900943882}, - {0.19515621126922889, 0.9663577672470134, 0.052248997892968774, 0.3368420822314041, 0.25591814564245663, - 0.31140858941730654, 0.6214194316782065, 0.898776506616156, 0.21908542333722314, 0.1721154555352078, - 0.8129941790492901, 0.11019299590688614, 0.9358894676906747, 0.5566948949725117, 0.5712632308813113, - 0.6198873277092072}, - {0.7553443433772105, 0.7149725143780661, 0.26342945108194404, 0.6073384956030843, 0.6682926680358615, - 0.9391571286734728, 0.6857282535520356, 0.8074713507427728, 0.9936095082352023, 0.7050591882477736, - 0.8307563345962672, 0.4504495350884602, 0.6669656135676392, 0.8229995389638302, 0.9724224050712397, - 0.4465681389737993, 0.830115912596753, 0.5758021742895939, 0.2555926327785295, 0.7229147142946879, - 0.19465587302731124, 0.6455358466934192, 0.9895816229678752, 0.27617594094673303, 0.27676552480841954, - 0.950721135261892, 0.06159792234410011, 0.25388932381509377, 0.027723112627898172, 0.6255532725359773, - 0.8145203264634711, 0.8254031856352116, 0.25282878966017297, 0.5652351235601151, 0.43847171025625065, - 0.2970657957526097, 0.03860912616964929, 0.9667833637002887, 0.8313877744010215, 0.8064932753796599, - 0.04382793626063919, 0.23236021244230354, 0.9946493719154478, 0.9442462780462477, 0.34190146836022484, - 0.4801148300313609, 0.4347269847824623, 0.1934175418869568, 0.9493835173004894, 0.008847061440726112, - 0.43992481101923, 0.9975703488985399, 0.20833318425512648, 0.39400365256608416, 0.43378030144507285, - 0.2626310071775566, 0.35102954497285954, 0.9000857989597337, 0.960793699725036, 0.4818308285625894, - 0.258367005138464, 0.7955496343862104, 0.38227049297061555, 0.5309262232624709}, - {0.030027811041104635, 0.24756223021053136, 0.6932959203102655, 0.31779244778670823}, - {0.057812427817348744, 0.48563678221308515, 0.6385926917596327, 0.06866424557822048, 0.7286057309387103, - 0.578198895275423, 0.35806649041869165, 0.17544934833578907, 0.9546365831179597, 0.7608272984745191, - 0.18047691393328225, 0.6719741912319742, 0.3254545042940935, 0.5391892808596687, 0.4610969186702265, - 0.6058329787299822}, - {0.5731193504743051, 0.8285838565973793, 0.033091935005607365, 0.9929008886851187, 0.2702292788833681, - 0.17372141606262959, 0.24616279825199905, 0.759464525093345, 0.37795458115492475, 0.7612050359964757, - 0.3364320358467179, 0.8518930134503825, 0.639680370851991, 0.5647886652079595, 0.06383168132690153, - 0.941871175081797}, - {0.10654574064225608, 0.18112904630201787, 0.7817037292161019, 0.748279488764987, 0.7485017168905912, - 0.4270901026653211, 0.5037981228936685, 0.8413518361670483, 0.8918350666826977, 0.8960201986678724, - 0.04380182167274471, 0.36899360872947595, 0.4792421777277245, 0.09414311412615073, 0.9810071872876617, - 0.9701739706435585, 0.566192946273844, 0.4761038133937181, 0.07930270657906191, 0.6958808238326885, - 0.07288965713564122, 0.32843359695282626, 0.27790842403879157, 0.6387041119338002, 0.15788528537840396, - 0.2667380867035477, 0.7715682128215527, 0.09996918315097514, 0.2690373433561819, 0.1530087585637756, - 0.3879254571666868, 0.3251278338384528, 0.2663572874336204, 0.5826546948845359, 0.8831125706700407, - 0.5502460958047991, 0.4710428569771352, 0.7530740137353021, 0.9754266619245848, 0.377421578189048, - 0.01652242589646613, 0.8244149593142929, 0.6820110251541992, 0.4002296116241395, 0.2955913658044953, - 0.7362095700182021, 0.40770234279226714, 0.87172045299516, 0.039230936916690995, 0.02750586292612267, - 0.10078915278495215, 0.8983285228828739, 0.32618356004699833, 0.6886593271816672, 0.07577625227317097, - 0.3447942876325266, 0.39914263324350274, 0.827412575862219, 0.34354508675330786, 0.09570201879504958, - 0.3370302990354914, 0.8158265013181055, 0.6293777092101274, 0.431976260298389}, - {0.09433683850154906, 0.6852026530097579, 0.32427852025540604, 0.25548685811214333}, - {0.04917788114994759, 0.3254526123103906, 0.007583733861966979, 0.7225054664397857}}; - - double[][][][] expectedOutputs = { - {{{2.810273443180434, 0.5315583899556339}, {-0.20450648028789575, -0.43732964918644013},}, - {{1.896089282928628, 1.1318539117217823}, {-0.03276314452169382, 0.3371527815133526},},}, - {{{8.029586903023583, 1.4783572816608272, 0.6711701068551449, -2.0728968306396376}, - {0.5021953651101563, -2.5648553213785314, 0.29423626194475117, 0.4012601985906169}, - {-1.2849021209400728, 0.6671323763730415, -0.6695725808397565, -1.1712843044117673}, - {2.108868551493044, 1.0843197356522687, 1.0867961189703699, -0.35053844891456276},}, - {{7.05912751036381, 0.5291423797442222, 0.4427197585821442, -0.755496055817138}, - {-0.30418687059677985, -2.2598935800730793, 0.6047902140223604, -1.5847243484575326}, - {0.25274774363624575, 0.05354074350183502, -0.46293891370557894, -1.4928446525217676}, - {0.04102445584853265, 1.2150529213724437, -0.3501635847389326, -1.0905422962407672},},}, - {{{6.568215549889579, -1.2298598996976158, 2.2138636830117013, -1.3116154891143874}, - {-1.1345702156384125, 0.1358688749140914, -0.869135880783189, -1.2945083094997623}, - {-0.23490362681105115, 1.8841337467288004, -1.290356324016746, -0.7998632424301513}, - {0.057854280160745564, 1.3009331869650396, 0.4577001084229444, 0.4609374698297427},}, - {{7.6362507070770524, -0.5021300289612729, -0.30830053219433307, -0.40162315416311345}, - {0.00532353080901915, 1.0369754448667339, -2.483776588979377, -0.016217813328190278}, - {-1.9062644821386074, -0.3728019954908228, -0.3037464465492685, -0.527450990728018}, - {0.46711047881499734, 0.4414987825805363, -1.1273549935419012, 1.4850074622342282},},}, - {{{26.68609298551157, -1.3338953635792201, 0.4020594424454147, 0.7401299103272476, -2.810554548459751, - -1.2778878008097723, -2.434717541683333, 1.8448958370317132}, - {1.5124772568141867, 2.997435519224954, 0.850391793491705, -1.409268535773998, 0.7298732550934647, - -1.0941883198448044, -0.4717054661204718, 2.2295601902248148}, - {0.9413326138415741, -3.5206438979732253, -1.860288065902871, -2.154762617920012, 1.4998965860283968, - 4.037874687522323, -2.97033202289703, -1.0678593057556733}, - {1.6739716993045417, -0.05711258741013203, -2.3676553799709974, 0.5456868972025865, 0.1948281831859553, - 0.7526107332947336, 5.065447419515738, 0.020456210203669833}, - {-0.6956713490957522, -1.8296524399484828, -3.1697164039855377, 2.237563147861894, 1.4551539353995173, - -0.19554352732365277, -0.4636431658812785, 2.8849067509584243}, - {1.2874212252086117, -2.007455023954, 0.41828409822144785, 2.1560540097992273, -0.9444730547029133, - -1.9570758407351865, -2.7449964653128944, -2.1931578153825004}, - {-1.1727630961580466, -0.4626944940820328, -0.6026604126017501, -1.147197715423295, -4.969175678925014, - -1.8568706158045902, -4.704146412253633, 3.2664484067588457}, - {-2.0576765512500503, 0.06751753454362419, 0.412051207702436, 0.026723509175442417, 0.8659815323685047, - 0.6298326096441218, -1.0225568132581624, -1.5743660274808011},}, - {{35.87455968396184, -1.5248270601889629, -3.804141676368115, 1.8347304094358663, -2.5797852364328016, - 4.556415677676634, -2.059620257585239, 3.3259090771947486}, - {3.8330193771856442, 3.96923392685639, -2.4257908732841234, -0.5143874873871535, 2.544553958429333, - 5.542586595696317, 4.951043571220989, -1.6001504185486546}, - {1.572704438375391, 2.68337930664312, -0.8033985360422281, -2.6558624309276784, -0.40085422767947554, - -4.000393284174368, -4.331102876667192, 3.506899629433532}, - {-2.4680885926531975, -0.8695810917508298, 2.9456617602301707, -0.24732914730729616, 0.3430542822308015, - 3.485849838657556, 3.9649335464206823, -1.680341105583727}, - {-0.22764415191794996, -0.4951316522431507, 4.061903443125903, -0.6424984523403212, - -0.12529040791295087, 5.354293685375573, 0.040525918509912495, 1.0133620066630307}, - {0.6469092912664327, -1.9089096532567051, -3.0326019092676626, -3.6447657932795416, -2.122099108477178, - -0.7865053946835596, -0.1566125392915363, 1.984921157087272}, - {1.3348166868775824, -1.2189282274599857, 1.2479841062752444, -0.948271682640534, -3.975769852998465, - -1.562443050085742, 1.6163117823648547, -4.4062925310517995}, - {2.9675969104598394, -3.280556379469013, 2.3899027415100598, -1.6067363579675273, 0.7470324060379839, - -2.499741728611057, 2.986670707313758, 1.6217252291607327},},}, - {{{2.193079441145362, -0.41181283923662215}, {-0.14081174003275643, 0.9465030238449373},}, - {{1.2886784093486097, 0.15796905335413058}, {-0.7334983268453378, -0.593037891692984},},}, - {{{8.066703633447759, 0.35776587504447654, -0.8975456466084525, -1.3260971108489834}, - {1.2925550450271355, 0.7471970785662574, 1.8523195957640421, -0.09130567177735105}, - {0.75069086903558, 0.45287128407866684, 1.1193031551551829, 0.11343169446248846}, - {1.4750614801978492, -0.4474301227850148, 0.46932996702100105, -0.9214050900106854},}, - {{7.590515281648607, -0.08161444013345664, -0.18103075974871663, 0.9381669029060151}, - {-1.5770093406180952, -2.7484477676036216, 1.3742861198499816, -1.2015040141484536}, - {0.046726986603437215, -2.150769239162087, -0.1301370419904344, 2.067734397359103}, - {-1.057408338160801, -0.5695893624855741, -0.4947019509681271, -0.9002185882701961},},}, - {{{8.16568534963898, -1.440928805449021, -0.5466902254014514, 0.9947324514433773}, - {-0.6989972691812494, -1.187942183202632, -1.062436068043263, 0.09504092090567329}, - {-0.3794316462577032, -0.523294612518167, 2.7973454035250693, -1.9389358312439806}, - {0.8221904791733652, 1.051347229425449, -1.835143916223776, 0.0628803438524741},}, - {{8.414930607970902, 1.0408479366310592, -3.3339265443792727, 1.3220823252356668}, - {-0.390009888242036, -0.7239135908698271, -0.5199660526428092, 1.1500729000277474}, - {1.0954307864509207, -1.1232199355230432, -0.8940432401163299, 1.086489516763935}, - {0.590432616869855, -0.5703843580205217, -0.11315800207193116, 2.138244529504565},},}, - {{{35.97203267785346, 0.45863783607284137, 1.173056641524758, 0.7826325498969262, -0.7567714479514733, - 0.8900030139481167, 2.3340075668593485, 2.7783597078640225}, - {-1.8297969735165558, 2.273178623633307, -0.48372099384482214, -0.40852587671049156, 3.760728469728649, - -0.7610308335136011, 1.2919699452742253, 4.670040243629213}, - {4.8372690355107135, -1.3261208568775944, 1.46633732066654, -0.0992228827598397, -1.9223527786243282, - 0.44458038637838815, 1.5533185398931715, 0.24496224499509434}, - {1.6661974146730905, 3.8314516505325447, -1.589204977232685, -2.9696832529162926, 1.079869594848176, - 2.8153541217824465, 3.0882863865289134, -2.28027259208448}, - {-1.0570779237084942, 2.806843738194792, -1.4276915996739796, -3.4905691035061417, -2.230138814152109, - -2.872817891174476, 3.932782623656472, 1.1162539578269415}, - {4.289222018755856, 2.105500347856227, -1.0762009261508743, -1.6228254057953164, -2.190533559398732, - 1.1004383893378056, 1.1709877234229678, 3.1232851514701316}, - {-0.45942227177196493, -1.819905457869288, 1.4446474205856596, 0.5520635899236316, 1.3655329844463444, - 2.67212745690105, -0.57694981659637, -3.6005960797530303}, - {-1.743365079415264, -2.335615239269563, -1.8778197199771323, 1.4330640847642324, -0.6075926305809934, - -1.8295061379841517, 0.5375950122938771, -0.668826591213622},}, - {{30.10531167057931, -3.4682674523063, -4.122859779991433, -2.4814276275979648, -3.3693361452179884, - -0.08565925201122448, -0.8401905476029238, 3.8121277138883807}, - {-0.028543509160374403, 1.4086455375421374, 7.027677724316744, -0.8029732853849363, 0.21508257722145652, - -1.2788348742349065, 0.7592532617840794, 0.09119198095300907}, - {2.4233994453629304, -3.012051635580196, 1.1558640736544619, -4.573157481233569, 4.20161895289186, - -1.204816459987149, -1.6145773913793897, -1.5849104998999013}, - {-5.4157739368994235, -3.7882855945306204, 2.7772761792419294, -2.4584113040850095, -2.069213530724667, - -0.5478843826867934, -1.6160901693215453, -0.6568154923833014}, - {-0.43647261868644627, -5.5334868871320335, 0.40365819731692065, 2.150018598987577, -1.2004536158862775, - 2.4388808649611087, 0.23590045802305715, -1.9545129970611796}, - {1.6309381678509949, -1.9880205240060747, -1.7739681748314111, -0.29204772903330056, 0.8738754999325199, - 0.2691425045145516, -1.2056513315502992, 1.071397699534742}, - {4.698703675388435, -0.7957304428932623, -4.7711315599977855, 0.09057617743728708, 1.4691381287521628, - -0.30099768897637336, -2.8594113121092493, 2.9571503367352006}, - {1.729635373900504, 1.4043331763077305, 0.4195010449027432, 2.9588786679014305, -0.579121181023122, - -2.422844062570481, -0.8625209856866667, 0.0362011748846961},},}, - {{{1.8880869312050084, 0.17014705148566178}, {0.1511761270942118, 1.1057152930560752},}, - {{1.3593048698788563, -0.5220741523649461}, {0.1997741131437576, -0.6596574766514716},},}, - {{{0.5726217776053402, -0.3670970767855093}, {0.09861077665339368, -0.05460772804233427},}, - {{1.104719693762091, -0.9911964637382618}, {-0.35545870684141456, 0.4386470014173758},},} - - }; - - @Test - public void testFft2dWithGeneratedData() { - for(int testIndex = 0; testIndex < reInputs.length; testIndex++) { - double[] re = reInputs[testIndex]; - double[] im = imInputs[testIndex]; - double[][][] expected = expectedOutputs[testIndex]; - - int sideLength = (int) Math.sqrt(re.length); - - // Your FFT implementation - fft(re, im, sideLength, sideLength); - - // Assert results - for(int i = 0; i < sideLength; i++) { - for(int j = 0; j < sideLength; j++) { - assertComplexEquals("Mismatch at [" + i + "][" + j + "]", expected[0][i][j], expected[1][i][j], - re[i * sideLength + j], im[i * sideLength + j]); - } - } - } - } - - @Test - public void testIfft2dWithGeneratedData() { - for(int testIndex = 0; testIndex < reInputs.length; testIndex++) { - double[] re = reInputs[testIndex]; - double[] im = imInputs[testIndex]; - double[][][] expected = expectedOutputs[testIndex]; - - int sideLength = (int) Math.sqrt(re.length); - - // Your IFFT implementation - ifft(re, im, sideLength, sideLength); - - // Assert results - for(int i = 0; i < sideLength; i++) { - for(int j = 0; j < sideLength; j++) { - assertComplexEquals("Mismatch at [" + i + "][" + j + "]", expected[0][i][j], expected[1][i][j], - re[i * sideLength + j], im[i * sideLength + j]); - } - } - } - } -} From 7f71ac9927ea94fdb25aad5ff77b686c6c66d58c Mon Sep 17 00:00:00 2001 From: Jessica Priebe Date: Wed, 24 Jan 2024 01:32:18 +0100 Subject: [PATCH 049/133] added java doc fft_one_dim --- .../sysds/runtime/matrix/data/LibMatrixFourier.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java index b53f7b68f5b..19e27b983f2 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java @@ -112,6 +112,19 @@ public static void ifft(double[] re, double[] im, int rows, int cols) { } + /** + * Function to perform one-dimensional FFT on two given double arrays. The first one represents the real values and + * the second one the imaginary values. Both arrays get updated and contain the result. + * + * @param re array representing the real values + * @param im array representing the imaginary values + * @param re_inter array for the real values of intermediate results + * @param im_inter array for the imaginary values of intermediate results + * @param start start index (incl.) + * @param stop stop index (excl.) + * @param num number of values used for fft + * @param minStep step size between values used for fft + */ public static void fft_one_dim(double[] re, double[] im, double[] re_inter, double[] im_inter, int start, int stop, int num, int minStep) { From a67f00660d46b321bc73cc38a559ee603e05396b Mon Sep 17 00:00:00 2001 From: Frederic Zoepffel Date: Thu, 8 Feb 2024 17:51:44 +0100 Subject: [PATCH 050/133] reversed formatting in functionop --- .../org/apache/sysds/hops/FunctionOp.java | 828 +++++++++--------- 1 file changed, 404 insertions(+), 424 deletions(-) diff --git a/src/main/java/org/apache/sysds/hops/FunctionOp.java b/src/main/java/org/apache/sysds/hops/FunctionOp.java index 26b9cc353d2..2f42c170f0e 100644 --- a/src/main/java/org/apache/sysds/hops/FunctionOp.java +++ b/src/main/java/org/apache/sysds/hops/FunctionOp.java @@ -17,194 +17,250 @@ * under the License. */ -package org.apache.sysds.hops; - -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; - -import org.apache.sysds.api.DMLScript; -import org.apache.sysds.lops.Compression; -import org.apache.sysds.lops.FunctionCallCP; -import org.apache.sysds.lops.Lop; -import org.apache.sysds.common.Types.ExecType; -import org.apache.sysds.parser.DMLProgram; -import org.apache.sysds.common.Types.DataType; -import org.apache.sysds.common.Types.ValueType; -import org.apache.sysds.runtime.compress.SingletonLookupHashMap; -import org.apache.sysds.runtime.controlprogram.Program; -import org.apache.sysds.runtime.controlprogram.parfor.opt.CostEstimatorHops; -import org.apache.sysds.runtime.meta.DataCharacteristics; - -/** - * This FunctionOp represents the call to a DML-bodied or external function. - * - * Note: Currently, we support expressions in function arguments along with function calls in expressions with single - * outputs, leaving multiple outputs handling as it is. - */ -public class FunctionOp extends Hop { - public enum FunctionType { - DML, MULTIRETURN_BUILTIN, UNKNOWN - } - - public static final String OPCODE = "fcall"; - - private FunctionType _type = null; - private String _fnamespace = null; - private String _fname = null; - private boolean _opt = true; // call to optimized/unoptimized - private boolean _pseudo = false; - - private String[] _inputNames = null; // A,B in C = foo(A=X, B=Y) - private String[] _outputNames = null; // C in C = foo(A=X, B=Y) - private ArrayList _outputHops = null; - - private FunctionOp() { - // default constructor for clone - } - - public FunctionOp(FunctionType type, String fnamespace, String fname, String[] inputNames, List inputs, - String[] outputNames, ArrayList outputHops) { - this(type, fnamespace, fname, inputNames, inputs, outputNames, false); - _outputHops = outputHops; - } - - public FunctionOp(FunctionType type, String fnamespace, String fname, String[] inputNames, List inputs, - String[] outputNames, boolean singleOut) { - this(type, fnamespace, fname, inputNames, inputs, outputNames, singleOut, false); - } - - public FunctionOp(FunctionType type, String fnamespace, String fname, String[] inputNames, List inputs, - String[] outputNames, boolean singleOut, boolean pseudo) { - super(fnamespace + Program.KEY_DELIM + fname, DataType.UNKNOWN, ValueType.UNKNOWN); - - _type = type; - _fnamespace = fnamespace; - _fname = fname; - _inputNames = inputNames; - _outputNames = outputNames; - _pseudo = pseudo; - - for(Hop in : inputs) { - getInput().add(in); - in.getParent().add(this); - } - } - - /** FunctionOps may have any number of inputs. */ - @Override - public void checkArity() { - } - - public String getFunctionKey() { - return DMLProgram.constructFunctionKey(getFunctionNamespace(), getFunctionName()); - } - - public String getFunctionNamespace() { - return _fnamespace; - } - - public String getFunctionName() { - return _fname; - } - - public void setFunctionName(String fname) { - _fname = fname; - } - - public void setFunctionNamespace(String fnamespace) { - _fnamespace = fnamespace; - } - - public void setInputVariableNames(String[] names) { - _inputNames = names; - } - - public ArrayList getOutputs() { - return _outputHops; - } - - public String[] getInputVariableNames() { - return _inputNames; - } - - public String[] getOutputVariableNames() { - return _outputNames; - } - - public boolean containsOutput(String varname) { - return Arrays.stream(getOutputVariableNames()).anyMatch(outName -> outName.equals(varname)); - } - - public FunctionType getFunctionType() { - return _type; - } - - public void setCallOptimized(boolean opt) { - _opt = opt; - } - - public boolean isPseudoFunctionCall() { - return _pseudo; - } - - @Override - public boolean allowsAllExecTypes() { - return false; - } - - @Override - public void computeMemEstimate(MemoTable memo) { - // overwrites default hops behavior - - if(_type == FunctionType.DML) - _memEstimate = 1; // minimal mem estimate - else if(_type == FunctionType.UNKNOWN) - _memEstimate = CostEstimatorHops.DEFAULT_MEM_SP; - else if(_type == FunctionType.MULTIRETURN_BUILTIN) { - boolean outputDimsKnown = true; - for(Hop out : getOutputs()) { - outputDimsKnown &= out.dimsKnown(); - } - if(outputDimsKnown) { - long lnnz = (getNnz() >= 0) ? getNnz() : getLength(); - _outputMemEstimate = computeOutputMemEstimate(getDim1(), getDim2(), lnnz); - _processingMemEstimate = computeIntermediateMemEstimate(getDim1(), getDim2(), lnnz); - } - _memEstimate = getInputOutputSize(); - } - } - - @Override - protected double computeOutputMemEstimate(long dim1, long dim2, long nnz) { - if(getFunctionType() != FunctionType.MULTIRETURN_BUILTIN) - throw new RuntimeException("Invalid call of computeOutputMemEstimate in FunctionOp."); - else { - if(getFunctionName().equalsIgnoreCase("qr")) { - // upper-triangular and lower-triangular matrices - long outputH = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(0).getDim1(), - getOutputs().get(0).getDim2(), 0.5); - long outputR = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(1).getDim1(), - getOutputs().get(1).getDim2(), 0.5); - return outputH + outputR; - } - else if(getFunctionName().equalsIgnoreCase("lu")) { - // upper-triangular and lower-triangular matrices - long outputP = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(1).getDim1(), - getOutputs().get(1).getDim2(), 1.0 / getOutputs().get(1).getDim2()); - long outputL = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(0).getDim1(), - getOutputs().get(0).getDim2(), 0.5); - long outputU = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(1).getDim1(), - getOutputs().get(1).getDim2(), 0.5); - return outputL + outputU + outputP; + package org.apache.sysds.hops; + + import java.util.ArrayList; + import java.util.Arrays; + import java.util.List; + + import org.apache.sysds.api.DMLScript; + import org.apache.sysds.lops.Compression; + import org.apache.sysds.lops.FunctionCallCP; + import org.apache.sysds.lops.Lop; + import org.apache.sysds.common.Types.ExecType; + import org.apache.sysds.parser.DMLProgram; + import org.apache.sysds.common.Types.DataType; + import org.apache.sysds.common.Types.ValueType; + import org.apache.sysds.runtime.compress.SingletonLookupHashMap; + import org.apache.sysds.runtime.controlprogram.Program; + import org.apache.sysds.runtime.controlprogram.parfor.opt.CostEstimatorHops; + import org.apache.sysds.runtime.meta.DataCharacteristics; + + /** + * This FunctionOp represents the call to a DML-bodied or external function. + * + * Note: Currently, we support expressions in function arguments along with function calls + * in expressions with single outputs, leaving multiple outputs handling as it is. + */ + public class FunctionOp extends Hop + { + public enum FunctionType{ + DML, + MULTIRETURN_BUILTIN, + UNKNOWN + } + + public static final String OPCODE = "fcall"; + + private FunctionType _type = null; + private String _fnamespace = null; + private String _fname = null; + private boolean _opt = true; //call to optimized/unoptimized + private boolean _pseudo = false; + + private String[] _inputNames = null; // A,B in C = foo(A=X, B=Y) + private String[] _outputNames = null; // C in C = foo(A=X, B=Y) + private ArrayList _outputHops = null; + + private FunctionOp() { + //default constructor for clone + } + + public FunctionOp(FunctionType type, String fnamespace, String fname, String[] inputNames, List inputs, String[] outputNames, ArrayList outputHops) { + this(type, fnamespace, fname, inputNames, inputs, outputNames, false); + _outputHops = outputHops; + } + public FunctionOp(FunctionType type, String fnamespace, String fname, String[] inputNames, List inputs, String[] outputNames, boolean singleOut) { + this(type, fnamespace, fname, inputNames, inputs, outputNames, singleOut, false); + } + + public FunctionOp(FunctionType type, String fnamespace, String fname, String[] inputNames, List inputs, String[] outputNames, boolean singleOut, boolean pseudo) + { + super(fnamespace + Program.KEY_DELIM + fname, DataType.UNKNOWN, ValueType.UNKNOWN ); + + _type = type; + _fnamespace = fnamespace; + _fname = fname; + _inputNames = inputNames; + _outputNames = outputNames; + _pseudo = pseudo; + + for( Hop in : inputs ) { + getInput().add(in); + in.getParent().add(this); + } + } + + /** FunctionOps may have any number of inputs. */ + @Override + public void checkArity() {} + + public String getFunctionKey() { + return DMLProgram.constructFunctionKey( + getFunctionNamespace(), getFunctionName()); + } + + public String getFunctionNamespace() { + return _fnamespace; + } + + public String getFunctionName() { + return _fname; + } + + public void setFunctionName( String fname ) { + _fname = fname; + } + + public void setFunctionNamespace( String fnamespace ) { + _fnamespace = fnamespace; + } + + public void setInputVariableNames(String[] names) { + _inputNames = names; + } + + public ArrayList getOutputs() { + return _outputHops; + } + + public String[] getInputVariableNames() { + return _inputNames; + } + + public String[] getOutputVariableNames() { + return _outputNames; + } + + public boolean containsOutput(String varname) { + return Arrays.stream(getOutputVariableNames()) + .anyMatch(outName -> outName.equals(varname)); + } + + public FunctionType getFunctionType() { + return _type; + } + + public void setCallOptimized(boolean opt) { + _opt = opt; + } + + public boolean isPseudoFunctionCall() { + return _pseudo; + } + + @Override + public boolean allowsAllExecTypes() { + return false; + } + + @Override + public void computeMemEstimate( MemoTable memo ) + { + //overwrites default hops behavior + + if( _type == FunctionType.DML ) + _memEstimate = 1; //minimal mem estimate + else if( _type == FunctionType.UNKNOWN ) + _memEstimate = CostEstimatorHops.DEFAULT_MEM_SP; + else if ( _type == FunctionType.MULTIRETURN_BUILTIN ) { + boolean outputDimsKnown = true; + for(Hop out : getOutputs()){ + outputDimsKnown &= out.dimsKnown(); + } + if( outputDimsKnown ) { + long lnnz = (getNnz()>=0)?getNnz():getLength(); + _outputMemEstimate = computeOutputMemEstimate(getDim1(), getDim2(), lnnz); + _processingMemEstimate = computeIntermediateMemEstimate(getDim1(), getDim2(), lnnz); + } + _memEstimate = getInputOutputSize(); + } + } + + @Override + protected double computeOutputMemEstimate( long dim1, long dim2, long nnz ) + { + if ( getFunctionType() != FunctionType.MULTIRETURN_BUILTIN ) + throw new RuntimeException("Invalid call of computeOutputMemEstimate in FunctionOp."); + else { + if ( getFunctionName().equalsIgnoreCase("qr") ) { + // upper-triangular and lower-triangular matrices + long outputH = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(0).getDim1(), getOutputs().get(0).getDim2(), 0.5); + long outputR = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(1).getDim1(), getOutputs().get(1).getDim2(), 0.5); + return outputH+outputR; + } + else if ( getFunctionName().equalsIgnoreCase("lu") ) { + // upper-triangular and lower-triangular matrices + long outputP = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(1).getDim1(), getOutputs().get(1).getDim2(), 1.0/getOutputs().get(1).getDim2()); + long outputL = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(0).getDim1(), getOutputs().get(0).getDim2(), 0.5); + long outputU = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(1).getDim1(), getOutputs().get(1).getDim2(), 0.5); + return outputL+outputU+outputP; + } + else if ( getFunctionName().equalsIgnoreCase("eigen") ) { + long outputVectors = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(0).getDim1(), getOutputs().get(0).getDim2(), 1.0); + long outputValues = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(1).getDim1(), 1, 1.0); + return outputVectors+outputValues; + } + else if(getFunctionName().equalsIgnoreCase("fft")) { + // 2 matrices of size same as the input + return 2 * OptimizerUtils.estimateSizeExactSparsity(getInput().get(0).getDim1(), + getInput().get(0).getDim2(), 1.0); } - else if(getFunctionName().equalsIgnoreCase("eigen")) { - long outputVectors = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(0).getDim1(), - getOutputs().get(0).getDim2(), 1.0); - long outputValues = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(1).getDim1(), 1, 1.0); - return outputVectors + outputValues; + else if(getFunctionName().equalsIgnoreCase("ifft")) { + // 2 matrices of size same as the input + return 2 * OptimizerUtils.estimateSizeExactSparsity(getInput().get(0).getDim1(), + getInput().get(0).getDim2(), 1.0); } - else if(getFunctionName().equalsIgnoreCase("fft")) { + else if ( getFunctionName().equalsIgnoreCase("lstm") || getFunctionName().equalsIgnoreCase("lstm_backward") ) { + // TODO: To allow for initial version to always run on the GPU + return 0; + } + else if ( getFunctionName().equalsIgnoreCase("batch_norm2d") || getFunctionName().equalsIgnoreCase("batch_norm2d_train")) { + return OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(0).getDim1(), getOutputs().get(0).getDim2(), 1.0) + + OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(1).getDim1(), getOutputs().get(1).getDim2(), 1.0) + + OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(2).getDim1(), getOutputs().get(2).getDim2(), 1.0) + + OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(3).getDim1(), getOutputs().get(3).getDim2(), 1.0) + + OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(4).getDim1(), getOutputs().get(4).getDim2(), 1.0); + } + else if ( getFunctionName().equalsIgnoreCase("batch_norm2d_test") ) { + return OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(0).getDim1(), getOutputs().get(0).getDim2(), 1.0); + } + else if ( getFunctionName().equalsIgnoreCase("batch_norm2d_backward") ) { + return OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(0).getDim1(), getOutputs().get(0).getDim2(), 1.0) + + OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(1).getDim1(), getOutputs().get(1).getDim2(), 1.0) + + OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(2).getDim1(), getOutputs().get(2).getDim2(), 1.0); + } + else if ( getFunctionName().equalsIgnoreCase("svd") ) { + long outputU = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(0).getDim1(), getOutputs().get(0).getDim2(), 1.0); + long outputSigma = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(1).getDim1(), getOutputs().get(1).getDim2(), 1.0); + long outputV = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(2).getDim1(), getOutputs().get(2).getDim2(), 1.0); + return outputU+outputSigma+outputV; + } + else + throw new RuntimeException("Invalid call of computeOutputMemEstimate in FunctionOp."); + } + } + + @Override + protected double computeIntermediateMemEstimate( long dim1, long dim2, long nnz ) + { + if ( getFunctionType() != FunctionType.MULTIRETURN_BUILTIN ) + throw new RuntimeException("Invalid call of computeIntermediateMemEstimate in FunctionOp."); + else { + if ( getFunctionName().equalsIgnoreCase("qr") ) { + // matrix of size same as the input + return OptimizerUtils.estimateSizeExactSparsity(getInput().get(0).getDim1(), getInput().get(0).getDim2(), 1.0); + } + else if ( getFunctionName().equalsIgnoreCase("lu")) { + // 1D vector + return OptimizerUtils.estimateSizeExactSparsity(getInput().get(0).getDim1(), 1, 1.0); + } + else if ( getFunctionName().equalsIgnoreCase("eigen")) { + // One matrix of size original input and three 1D vectors (used to represent tridiagonal matrix) + return OptimizerUtils.estimateSizeExactSparsity(getInput().get(0).getDim1(), getInput().get(0).getDim2(), 1.0) + + 3*OptimizerUtils.estimateSizeExactSparsity(getInput().get(0).getDim1(), 1, 1.0); + } + else if(getFunctionName().equalsIgnoreCase("fft")) { long outputRe = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(0).getDim1(), getOutputs().get(0).getDim2(), 1.0); long outputIm = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(1).getDim1(), @@ -218,241 +274,165 @@ else if(getFunctionName().equalsIgnoreCase("ifft")) { getOutputs().get(1).getDim2(), 1.0); return outputRe + outputIm; } - else if(getFunctionName().equalsIgnoreCase("lstm") || getFunctionName().equalsIgnoreCase("lstm_backward")) { - // TODO: To allow for initial version to always run on the GPU - return 0; - } - else if(getFunctionName().equalsIgnoreCase("batch_norm2d") || - getFunctionName().equalsIgnoreCase("batch_norm2d_train")) { - return OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(0).getDim1(), - getOutputs().get(0).getDim2(), 1.0) + - OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(1).getDim1(), - getOutputs().get(1).getDim2(), 1.0) + - OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(2).getDim1(), - getOutputs().get(2).getDim2(), 1.0) + - OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(3).getDim1(), - getOutputs().get(3).getDim2(), 1.0) + - OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(4).getDim1(), - getOutputs().get(4).getDim2(), 1.0); - } - else if(getFunctionName().equalsIgnoreCase("batch_norm2d_test")) { - return OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(0).getDim1(), - getOutputs().get(0).getDim2(), 1.0); - } - else if(getFunctionName().equalsIgnoreCase("batch_norm2d_backward")) { - return OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(0).getDim1(), - getOutputs().get(0).getDim2(), 1.0) + - OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(1).getDim1(), - getOutputs().get(1).getDim2(), 1.0) + - OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(2).getDim1(), - getOutputs().get(2).getDim2(), 1.0); - } - else if(getFunctionName().equalsIgnoreCase("svd")) { - long outputU = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(0).getDim1(), - getOutputs().get(0).getDim2(), 1.0); - long outputSigma = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(1).getDim1(), - getOutputs().get(1).getDim2(), 1.0); - long outputV = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(2).getDim1(), - getOutputs().get(2).getDim2(), 1.0); - return outputU + outputSigma + outputV; - } - else - throw new RuntimeException("Invalid call of computeOutputMemEstimate in FunctionOp."); - } - } - - @Override - protected double computeIntermediateMemEstimate(long dim1, long dim2, long nnz) { - if(getFunctionType() != FunctionType.MULTIRETURN_BUILTIN) - throw new RuntimeException("Invalid call of computeIntermediateMemEstimate in FunctionOp."); - else { - if(getFunctionName().equalsIgnoreCase("qr")) { - // matrix of size same as the input - return OptimizerUtils.estimateSizeExactSparsity(getInput().get(0).getDim1(), - getInput().get(0).getDim2(), 1.0); - } - else if(getFunctionName().equalsIgnoreCase("lu")) { - // 1D vector - return OptimizerUtils.estimateSizeExactSparsity(getInput().get(0).getDim1(), 1, 1.0); - } - else if(getFunctionName().equalsIgnoreCase("eigen")) { - // One matrix of size original input and three 1D vectors (used to represent tridiagonal matrix) - return OptimizerUtils.estimateSizeExactSparsity(getInput().get(0).getDim1(), - getInput().get(0).getDim2(), 1.0) + - 3 * OptimizerUtils.estimateSizeExactSparsity(getInput().get(0).getDim1(), 1, 1.0); - } - else if(getFunctionName().equalsIgnoreCase("fft")) { - // 2 matrices of size same as the input - return 2 * OptimizerUtils.estimateSizeExactSparsity(getInput().get(0).getDim1(), - getInput().get(0).getDim2(), 1.0); - } - else if(getFunctionName().equalsIgnoreCase("ifft")) { - // 2 matrices of size same as the input - return 2 * OptimizerUtils.estimateSizeExactSparsity(getInput().get(0).getDim1(), - getInput().get(0).getDim2(), 1.0); - } - else if(getFunctionName().equalsIgnoreCase("batch_norm2d") || - getFunctionName().equalsIgnoreCase("batch_norm2d_backward") || - getFunctionName().equalsIgnoreCase("batch_norm2d_train") || - getFunctionName().equalsIgnoreCase("batch_norm2d_test")) { - return 0; - } - else if(getFunctionName().equalsIgnoreCase("lstm") || getFunctionName().equalsIgnoreCase("lstm_backward")) { - // TODO: To allow for initial version to always run on the GPU - return 0; - } - else if(getFunctionName().equalsIgnoreCase("svd")) { - double interOutput = OptimizerUtils.estimateSizeExactSparsity(1, getInput().get(0).getDim2(), 1.0); - return interOutput; - } - else - throw new RuntimeException("Invalid call of computeIntermediateMemEstimate in FunctionOp."); - } - } - - @Override - protected DataCharacteristics inferOutputCharacteristics(MemoTable memo) { - throw new RuntimeException("Invalid call of inferOutputCharacteristics in FunctionOp."); - } - - @Override - public boolean isGPUEnabled() { - if(getFunctionName().equalsIgnoreCase("lstm") || getFunctionName().equalsIgnoreCase("lstm_backward") || - getFunctionName().equalsIgnoreCase("batch_norm2d") || - getFunctionName().equalsIgnoreCase("batch_norm2d_backward") || - getFunctionName().equalsIgnoreCase("batch_norm2d_train") || - getFunctionName().equalsIgnoreCase("batch_norm2d_test")) - return true; - else - return false; - } - - @Override - public Lop constructLops() { - // return already created lops - if(getLops() != null) - return getLops(); - - ExecType et = optFindExecType(); - - // construct input lops (recursive) - ArrayList tmp = new ArrayList<>(); - for(Hop in : getInput()) - tmp.add(in.constructLops()); - - // construct function call - FunctionCallCP fcall = new FunctionCallCP(tmp, _fnamespace, _fname, _inputNames, _outputNames, _outputHops, - _opt, et); - setLineNumbers(fcall); - setLops(fcall); - - // note: no reblock lop because outputs directly bound - constructAndSetCompressionLopFunctionalIfRequired(et); - - return getLops(); - } - - protected void constructAndSetCompressionLopFunctionalIfRequired(ExecType et) { - if((requiresCompression()) && - ((FunctionCallCP) getLops()).getFunctionName().equalsIgnoreCase("transformencode")) { // xor - - // Lop matrixOut = lop.getFunctionOutputs().get(0); - Lop compressionInstruction = null; - - if(_compressedWorkloadTree != null) { - SingletonLookupHashMap m = SingletonLookupHashMap.getMap(); - int singletonID = m.put(_compressedWorkloadTree); - compressionInstruction = new Compression(getLops(), DataType.MATRIX, ValueType.FP64, et, singletonID); - } - else - compressionInstruction = new Compression(getLops(), DataType.MATRIX, ValueType.FP64, et, 0); - - setOutputDimensions(compressionInstruction); - setLineNumbers(compressionInstruction); - setLops(compressionInstruction); - - } - } - - @Override - public String getOpString() { - return OPCODE + " " + _fnamespace + " " + _fname; - } - - @Override - protected ExecType optFindExecType(boolean transitive) { - checkAndSetForcedPlatform(); - - if(getFunctionType() == FunctionType.MULTIRETURN_BUILTIN) { - boolean isBuiltinFunction = isBuiltinFunction(); - // check if there is sufficient memory to execute this function - if(isBuiltinFunction && getFunctionName().equalsIgnoreCase("transformencode")) { - _etype = ((_etypeForced == ExecType.SPARK || (getMemEstimate() >= OptimizerUtils.getLocalMemBudget() && - OptimizerUtils.isSparkExecutionMode())) ? ExecType.SPARK : ExecType.CP); - } - else if(isBuiltinFunction && - (getFunctionName().equalsIgnoreCase("lstm") || getFunctionName().equalsIgnoreCase("lstm_backward"))) { - if(!DMLScript.USE_ACCELERATOR) - throw new RuntimeException("The function " + getFunctionName() + " is only supported on GPU."); - _etype = ExecType.GPU; - } - else if(isBuiltinFunction && (getFunctionName().equalsIgnoreCase("batch_norm2d") || - getFunctionName().equalsIgnoreCase("batch_norm2d_backward"))) { - _etype = DMLScript.USE_ACCELERATOR ? ExecType.GPU : ExecType.CP; - } - else if(isBuiltinFunction && getFunctionName().equalsIgnoreCase("batch_norm2d_train")) { - // Only GPU implementation is supported - _etype = ExecType.GPU; - } - else { - // Since the memory estimate is only conservative, do not throw - // exception if the estimated memory is larger than the budget - // Nevertheless, memory estimates these functions are useful for - // other purposes, such as compiling parfor - _etype = ExecType.CP; - } - } - else { - // the actual function call is always CP - _etype = ExecType.CP; - } - - return _etype; - } - - private boolean isBuiltinFunction() { - return getFunctionNamespace().equals(DMLProgram.INTERNAL_NAMESPACE); - } - - @Override - public void refreshSizeInformation() { - // do nothing - } - - @Override - @SuppressWarnings("unchecked") - public Object clone() throws CloneNotSupportedException { - FunctionOp ret = new FunctionOp(); - - // copy generic attributes - ret.clone(this, false); - - // copy specific attributes - ret._type = _type; - ret._fnamespace = _fnamespace; - ret._fname = _fname; - ret._opt = _opt; - ret._inputNames = (_inputNames != null) ? _inputNames.clone() : null; - ret._outputNames = _outputNames.clone(); - if(_outputHops != null) - ret._outputHops = (ArrayList) _outputHops.clone(); - - return ret; - } - - @Override - public boolean compare(Hop that) { - return false; - } - -} + else if (getFunctionName().equalsIgnoreCase("batch_norm2d") || getFunctionName().equalsIgnoreCase("batch_norm2d_backward") || + getFunctionName().equalsIgnoreCase("batch_norm2d_train") || getFunctionName().equalsIgnoreCase("batch_norm2d_test")) { + return 0; + } + else if ( getFunctionName().equalsIgnoreCase("lstm") || getFunctionName().equalsIgnoreCase("lstm_backward") ) { + // TODO: To allow for initial version to always run on the GPU + return 0; + } + else if ( getFunctionName().equalsIgnoreCase("svd")) { + double interOutput = OptimizerUtils.estimateSizeExactSparsity(1, getInput().get(0).getDim2(), 1.0); + return interOutput; + } + else + throw new RuntimeException("Invalid call of computeIntermediateMemEstimate in FunctionOp."); + } + } + + @Override + protected DataCharacteristics inferOutputCharacteristics( MemoTable memo ) { + throw new RuntimeException("Invalid call of inferOutputCharacteristics in FunctionOp."); + } + + @Override + public boolean isGPUEnabled() { + if(getFunctionName().equalsIgnoreCase("lstm") || getFunctionName().equalsIgnoreCase("lstm_backward") || + getFunctionName().equalsIgnoreCase("batch_norm2d") || getFunctionName().equalsIgnoreCase("batch_norm2d_backward") || + getFunctionName().equalsIgnoreCase("batch_norm2d_train") || getFunctionName().equalsIgnoreCase("batch_norm2d_test")) + return true; + else + return false; + } + + @Override + public Lop constructLops() + { + //return already created lops + if( getLops() != null ) + return getLops(); + + ExecType et = optFindExecType(); + + //construct input lops (recursive) + ArrayList tmp = new ArrayList<>(); + for( Hop in : getInput() ) + tmp.add( in.constructLops() ); + + //construct function call + FunctionCallCP fcall = new FunctionCallCP(tmp, _fnamespace, _fname, _inputNames, _outputNames, _outputHops, _opt, et); + setLineNumbers(fcall); + setLops(fcall); + + //note: no reblock lop because outputs directly bound + constructAndSetCompressionLopFunctionalIfRequired(et); + + return getLops(); + } + + protected void constructAndSetCompressionLopFunctionalIfRequired(ExecType et) { + if((requiresCompression()) && ((FunctionCallCP) getLops()).getFunctionName().equalsIgnoreCase("transformencode")){ // xor + + // Lop matrixOut = lop.getFunctionOutputs().get(0); + Lop compressionInstruction = null; + + if(_compressedWorkloadTree != null) { + SingletonLookupHashMap m = SingletonLookupHashMap.getMap(); + int singletonID = m.put(_compressedWorkloadTree); + compressionInstruction = new Compression(getLops(), DataType.MATRIX, ValueType.FP64, et, singletonID); + } + else + compressionInstruction = new Compression(getLops(), DataType.MATRIX, ValueType.FP64, et, 0); + + + setOutputDimensions( compressionInstruction ); + setLineNumbers( compressionInstruction ); + setLops( compressionInstruction ); + + } + } + + + @Override + public String getOpString() { + return OPCODE + " " + _fnamespace + " " + _fname; + } + + @Override + protected ExecType optFindExecType(boolean transitive) + { + checkAndSetForcedPlatform(); + + if ( getFunctionType() == FunctionType.MULTIRETURN_BUILTIN ) { + boolean isBuiltinFunction = isBuiltinFunction(); + // check if there is sufficient memory to execute this function + if(isBuiltinFunction && getFunctionName().equalsIgnoreCase("transformencode") ) { + _etype = ((_etypeForced==ExecType.SPARK + || (getMemEstimate() >= OptimizerUtils.getLocalMemBudget() + && OptimizerUtils.isSparkExecutionMode())) ? ExecType.SPARK : ExecType.CP); + } + else if(isBuiltinFunction && (getFunctionName().equalsIgnoreCase("lstm") || getFunctionName().equalsIgnoreCase("lstm_backward"))) { + if(!DMLScript.USE_ACCELERATOR) + throw new RuntimeException("The function " + getFunctionName() + " is only supported on GPU."); + _etype = ExecType.GPU; + } + else if(isBuiltinFunction && (getFunctionName().equalsIgnoreCase("batch_norm2d") || getFunctionName().equalsIgnoreCase("batch_norm2d_backward"))) { + _etype = DMLScript.USE_ACCELERATOR ? ExecType.GPU : ExecType.CP; + } + else if(isBuiltinFunction && getFunctionName().equalsIgnoreCase("batch_norm2d_train")) { + // Only GPU implementation is supported + _etype = ExecType.GPU; + } + else { + // Since the memory estimate is only conservative, do not throw + // exception if the estimated memory is larger than the budget + // Nevertheless, memory estimates these functions are useful for + // other purposes, such as compiling parfor + _etype = ExecType.CP; + } + } + else { + // the actual function call is always CP + _etype = ExecType.CP; + } + + return _etype; + } + + private boolean isBuiltinFunction() { + return getFunctionNamespace().equals(DMLProgram.INTERNAL_NAMESPACE); + } + + @Override + public void refreshSizeInformation() { + //do nothing + } + + @Override + @SuppressWarnings("unchecked") + public Object clone() throws CloneNotSupportedException { + FunctionOp ret = new FunctionOp(); + + //copy generic attributes + ret.clone(this, false); + + //copy specific attributes + ret._type = _type; + ret._fnamespace = _fnamespace; + ret._fname = _fname; + ret._opt = _opt; + ret._inputNames = (_inputNames!=null) ? _inputNames.clone() : null; + ret._outputNames = _outputNames.clone(); + if( _outputHops != null ) + ret._outputHops = (ArrayList) _outputHops.clone(); + + return ret; + } + + @Override + public boolean compare(Hop that) { + return false; + } + + } \ No newline at end of file From 06a2f94e20015add5868bc57feafc7654aa6322e Mon Sep 17 00:00:00 2001 From: Frederic Zoepffel Date: Thu, 8 Feb 2024 18:03:16 +0100 Subject: [PATCH 051/133] Revert "reversed formatting in functionop" This reverts commit a67f00660d46b321bc73cc38a559ee603e05396b. --- .../org/apache/sysds/hops/FunctionOp.java | 828 +++++++++--------- 1 file changed, 424 insertions(+), 404 deletions(-) diff --git a/src/main/java/org/apache/sysds/hops/FunctionOp.java b/src/main/java/org/apache/sysds/hops/FunctionOp.java index 2f42c170f0e..26b9cc353d2 100644 --- a/src/main/java/org/apache/sysds/hops/FunctionOp.java +++ b/src/main/java/org/apache/sysds/hops/FunctionOp.java @@ -17,250 +17,194 @@ * under the License. */ - package org.apache.sysds.hops; - - import java.util.ArrayList; - import java.util.Arrays; - import java.util.List; - - import org.apache.sysds.api.DMLScript; - import org.apache.sysds.lops.Compression; - import org.apache.sysds.lops.FunctionCallCP; - import org.apache.sysds.lops.Lop; - import org.apache.sysds.common.Types.ExecType; - import org.apache.sysds.parser.DMLProgram; - import org.apache.sysds.common.Types.DataType; - import org.apache.sysds.common.Types.ValueType; - import org.apache.sysds.runtime.compress.SingletonLookupHashMap; - import org.apache.sysds.runtime.controlprogram.Program; - import org.apache.sysds.runtime.controlprogram.parfor.opt.CostEstimatorHops; - import org.apache.sysds.runtime.meta.DataCharacteristics; - - /** - * This FunctionOp represents the call to a DML-bodied or external function. - * - * Note: Currently, we support expressions in function arguments along with function calls - * in expressions with single outputs, leaving multiple outputs handling as it is. - */ - public class FunctionOp extends Hop - { - public enum FunctionType{ - DML, - MULTIRETURN_BUILTIN, - UNKNOWN - } - - public static final String OPCODE = "fcall"; - - private FunctionType _type = null; - private String _fnamespace = null; - private String _fname = null; - private boolean _opt = true; //call to optimized/unoptimized - private boolean _pseudo = false; - - private String[] _inputNames = null; // A,B in C = foo(A=X, B=Y) - private String[] _outputNames = null; // C in C = foo(A=X, B=Y) - private ArrayList _outputHops = null; - - private FunctionOp() { - //default constructor for clone - } - - public FunctionOp(FunctionType type, String fnamespace, String fname, String[] inputNames, List inputs, String[] outputNames, ArrayList outputHops) { - this(type, fnamespace, fname, inputNames, inputs, outputNames, false); - _outputHops = outputHops; - } - public FunctionOp(FunctionType type, String fnamespace, String fname, String[] inputNames, List inputs, String[] outputNames, boolean singleOut) { - this(type, fnamespace, fname, inputNames, inputs, outputNames, singleOut, false); - } - - public FunctionOp(FunctionType type, String fnamespace, String fname, String[] inputNames, List inputs, String[] outputNames, boolean singleOut, boolean pseudo) - { - super(fnamespace + Program.KEY_DELIM + fname, DataType.UNKNOWN, ValueType.UNKNOWN ); - - _type = type; - _fnamespace = fnamespace; - _fname = fname; - _inputNames = inputNames; - _outputNames = outputNames; - _pseudo = pseudo; - - for( Hop in : inputs ) { - getInput().add(in); - in.getParent().add(this); - } - } - - /** FunctionOps may have any number of inputs. */ - @Override - public void checkArity() {} - - public String getFunctionKey() { - return DMLProgram.constructFunctionKey( - getFunctionNamespace(), getFunctionName()); - } - - public String getFunctionNamespace() { - return _fnamespace; - } - - public String getFunctionName() { - return _fname; - } - - public void setFunctionName( String fname ) { - _fname = fname; - } - - public void setFunctionNamespace( String fnamespace ) { - _fnamespace = fnamespace; - } - - public void setInputVariableNames(String[] names) { - _inputNames = names; - } - - public ArrayList getOutputs() { - return _outputHops; - } - - public String[] getInputVariableNames() { - return _inputNames; - } - - public String[] getOutputVariableNames() { - return _outputNames; - } - - public boolean containsOutput(String varname) { - return Arrays.stream(getOutputVariableNames()) - .anyMatch(outName -> outName.equals(varname)); - } - - public FunctionType getFunctionType() { - return _type; - } - - public void setCallOptimized(boolean opt) { - _opt = opt; - } - - public boolean isPseudoFunctionCall() { - return _pseudo; - } - - @Override - public boolean allowsAllExecTypes() { - return false; - } - - @Override - public void computeMemEstimate( MemoTable memo ) - { - //overwrites default hops behavior - - if( _type == FunctionType.DML ) - _memEstimate = 1; //minimal mem estimate - else if( _type == FunctionType.UNKNOWN ) - _memEstimate = CostEstimatorHops.DEFAULT_MEM_SP; - else if ( _type == FunctionType.MULTIRETURN_BUILTIN ) { - boolean outputDimsKnown = true; - for(Hop out : getOutputs()){ - outputDimsKnown &= out.dimsKnown(); - } - if( outputDimsKnown ) { - long lnnz = (getNnz()>=0)?getNnz():getLength(); - _outputMemEstimate = computeOutputMemEstimate(getDim1(), getDim2(), lnnz); - _processingMemEstimate = computeIntermediateMemEstimate(getDim1(), getDim2(), lnnz); - } - _memEstimate = getInputOutputSize(); - } - } - - @Override - protected double computeOutputMemEstimate( long dim1, long dim2, long nnz ) - { - if ( getFunctionType() != FunctionType.MULTIRETURN_BUILTIN ) - throw new RuntimeException("Invalid call of computeOutputMemEstimate in FunctionOp."); - else { - if ( getFunctionName().equalsIgnoreCase("qr") ) { - // upper-triangular and lower-triangular matrices - long outputH = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(0).getDim1(), getOutputs().get(0).getDim2(), 0.5); - long outputR = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(1).getDim1(), getOutputs().get(1).getDim2(), 0.5); - return outputH+outputR; - } - else if ( getFunctionName().equalsIgnoreCase("lu") ) { - // upper-triangular and lower-triangular matrices - long outputP = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(1).getDim1(), getOutputs().get(1).getDim2(), 1.0/getOutputs().get(1).getDim2()); - long outputL = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(0).getDim1(), getOutputs().get(0).getDim2(), 0.5); - long outputU = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(1).getDim1(), getOutputs().get(1).getDim2(), 0.5); - return outputL+outputU+outputP; - } - else if ( getFunctionName().equalsIgnoreCase("eigen") ) { - long outputVectors = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(0).getDim1(), getOutputs().get(0).getDim2(), 1.0); - long outputValues = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(1).getDim1(), 1, 1.0); - return outputVectors+outputValues; - } - else if(getFunctionName().equalsIgnoreCase("fft")) { - // 2 matrices of size same as the input - return 2 * OptimizerUtils.estimateSizeExactSparsity(getInput().get(0).getDim1(), - getInput().get(0).getDim2(), 1.0); +package org.apache.sysds.hops; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import org.apache.sysds.api.DMLScript; +import org.apache.sysds.lops.Compression; +import org.apache.sysds.lops.FunctionCallCP; +import org.apache.sysds.lops.Lop; +import org.apache.sysds.common.Types.ExecType; +import org.apache.sysds.parser.DMLProgram; +import org.apache.sysds.common.Types.DataType; +import org.apache.sysds.common.Types.ValueType; +import org.apache.sysds.runtime.compress.SingletonLookupHashMap; +import org.apache.sysds.runtime.controlprogram.Program; +import org.apache.sysds.runtime.controlprogram.parfor.opt.CostEstimatorHops; +import org.apache.sysds.runtime.meta.DataCharacteristics; + +/** + * This FunctionOp represents the call to a DML-bodied or external function. + * + * Note: Currently, we support expressions in function arguments along with function calls in expressions with single + * outputs, leaving multiple outputs handling as it is. + */ +public class FunctionOp extends Hop { + public enum FunctionType { + DML, MULTIRETURN_BUILTIN, UNKNOWN + } + + public static final String OPCODE = "fcall"; + + private FunctionType _type = null; + private String _fnamespace = null; + private String _fname = null; + private boolean _opt = true; // call to optimized/unoptimized + private boolean _pseudo = false; + + private String[] _inputNames = null; // A,B in C = foo(A=X, B=Y) + private String[] _outputNames = null; // C in C = foo(A=X, B=Y) + private ArrayList _outputHops = null; + + private FunctionOp() { + // default constructor for clone + } + + public FunctionOp(FunctionType type, String fnamespace, String fname, String[] inputNames, List inputs, + String[] outputNames, ArrayList outputHops) { + this(type, fnamespace, fname, inputNames, inputs, outputNames, false); + _outputHops = outputHops; + } + + public FunctionOp(FunctionType type, String fnamespace, String fname, String[] inputNames, List inputs, + String[] outputNames, boolean singleOut) { + this(type, fnamespace, fname, inputNames, inputs, outputNames, singleOut, false); + } + + public FunctionOp(FunctionType type, String fnamespace, String fname, String[] inputNames, List inputs, + String[] outputNames, boolean singleOut, boolean pseudo) { + super(fnamespace + Program.KEY_DELIM + fname, DataType.UNKNOWN, ValueType.UNKNOWN); + + _type = type; + _fnamespace = fnamespace; + _fname = fname; + _inputNames = inputNames; + _outputNames = outputNames; + _pseudo = pseudo; + + for(Hop in : inputs) { + getInput().add(in); + in.getParent().add(this); + } + } + + /** FunctionOps may have any number of inputs. */ + @Override + public void checkArity() { + } + + public String getFunctionKey() { + return DMLProgram.constructFunctionKey(getFunctionNamespace(), getFunctionName()); + } + + public String getFunctionNamespace() { + return _fnamespace; + } + + public String getFunctionName() { + return _fname; + } + + public void setFunctionName(String fname) { + _fname = fname; + } + + public void setFunctionNamespace(String fnamespace) { + _fnamespace = fnamespace; + } + + public void setInputVariableNames(String[] names) { + _inputNames = names; + } + + public ArrayList getOutputs() { + return _outputHops; + } + + public String[] getInputVariableNames() { + return _inputNames; + } + + public String[] getOutputVariableNames() { + return _outputNames; + } + + public boolean containsOutput(String varname) { + return Arrays.stream(getOutputVariableNames()).anyMatch(outName -> outName.equals(varname)); + } + + public FunctionType getFunctionType() { + return _type; + } + + public void setCallOptimized(boolean opt) { + _opt = opt; + } + + public boolean isPseudoFunctionCall() { + return _pseudo; + } + + @Override + public boolean allowsAllExecTypes() { + return false; + } + + @Override + public void computeMemEstimate(MemoTable memo) { + // overwrites default hops behavior + + if(_type == FunctionType.DML) + _memEstimate = 1; // minimal mem estimate + else if(_type == FunctionType.UNKNOWN) + _memEstimate = CostEstimatorHops.DEFAULT_MEM_SP; + else if(_type == FunctionType.MULTIRETURN_BUILTIN) { + boolean outputDimsKnown = true; + for(Hop out : getOutputs()) { + outputDimsKnown &= out.dimsKnown(); } - else if(getFunctionName().equalsIgnoreCase("ifft")) { - // 2 matrices of size same as the input - return 2 * OptimizerUtils.estimateSizeExactSparsity(getInput().get(0).getDim1(), - getInput().get(0).getDim2(), 1.0); + if(outputDimsKnown) { + long lnnz = (getNnz() >= 0) ? getNnz() : getLength(); + _outputMemEstimate = computeOutputMemEstimate(getDim1(), getDim2(), lnnz); + _processingMemEstimate = computeIntermediateMemEstimate(getDim1(), getDim2(), lnnz); } - else if ( getFunctionName().equalsIgnoreCase("lstm") || getFunctionName().equalsIgnoreCase("lstm_backward") ) { - // TODO: To allow for initial version to always run on the GPU - return 0; - } - else if ( getFunctionName().equalsIgnoreCase("batch_norm2d") || getFunctionName().equalsIgnoreCase("batch_norm2d_train")) { - return OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(0).getDim1(), getOutputs().get(0).getDim2(), 1.0) + - OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(1).getDim1(), getOutputs().get(1).getDim2(), 1.0) + - OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(2).getDim1(), getOutputs().get(2).getDim2(), 1.0) + - OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(3).getDim1(), getOutputs().get(3).getDim2(), 1.0) + - OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(4).getDim1(), getOutputs().get(4).getDim2(), 1.0); - } - else if ( getFunctionName().equalsIgnoreCase("batch_norm2d_test") ) { - return OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(0).getDim1(), getOutputs().get(0).getDim2(), 1.0); - } - else if ( getFunctionName().equalsIgnoreCase("batch_norm2d_backward") ) { - return OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(0).getDim1(), getOutputs().get(0).getDim2(), 1.0) + - OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(1).getDim1(), getOutputs().get(1).getDim2(), 1.0) + - OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(2).getDim1(), getOutputs().get(2).getDim2(), 1.0); - } - else if ( getFunctionName().equalsIgnoreCase("svd") ) { - long outputU = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(0).getDim1(), getOutputs().get(0).getDim2(), 1.0); - long outputSigma = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(1).getDim1(), getOutputs().get(1).getDim2(), 1.0); - long outputV = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(2).getDim1(), getOutputs().get(2).getDim2(), 1.0); - return outputU+outputSigma+outputV; - } - else - throw new RuntimeException("Invalid call of computeOutputMemEstimate in FunctionOp."); - } - } - - @Override - protected double computeIntermediateMemEstimate( long dim1, long dim2, long nnz ) - { - if ( getFunctionType() != FunctionType.MULTIRETURN_BUILTIN ) - throw new RuntimeException("Invalid call of computeIntermediateMemEstimate in FunctionOp."); - else { - if ( getFunctionName().equalsIgnoreCase("qr") ) { - // matrix of size same as the input - return OptimizerUtils.estimateSizeExactSparsity(getInput().get(0).getDim1(), getInput().get(0).getDim2(), 1.0); - } - else if ( getFunctionName().equalsIgnoreCase("lu")) { - // 1D vector - return OptimizerUtils.estimateSizeExactSparsity(getInput().get(0).getDim1(), 1, 1.0); - } - else if ( getFunctionName().equalsIgnoreCase("eigen")) { - // One matrix of size original input and three 1D vectors (used to represent tridiagonal matrix) - return OptimizerUtils.estimateSizeExactSparsity(getInput().get(0).getDim1(), getInput().get(0).getDim2(), 1.0) - + 3*OptimizerUtils.estimateSizeExactSparsity(getInput().get(0).getDim1(), 1, 1.0); - } - else if(getFunctionName().equalsIgnoreCase("fft")) { + _memEstimate = getInputOutputSize(); + } + } + + @Override + protected double computeOutputMemEstimate(long dim1, long dim2, long nnz) { + if(getFunctionType() != FunctionType.MULTIRETURN_BUILTIN) + throw new RuntimeException("Invalid call of computeOutputMemEstimate in FunctionOp."); + else { + if(getFunctionName().equalsIgnoreCase("qr")) { + // upper-triangular and lower-triangular matrices + long outputH = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(0).getDim1(), + getOutputs().get(0).getDim2(), 0.5); + long outputR = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(1).getDim1(), + getOutputs().get(1).getDim2(), 0.5); + return outputH + outputR; + } + else if(getFunctionName().equalsIgnoreCase("lu")) { + // upper-triangular and lower-triangular matrices + long outputP = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(1).getDim1(), + getOutputs().get(1).getDim2(), 1.0 / getOutputs().get(1).getDim2()); + long outputL = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(0).getDim1(), + getOutputs().get(0).getDim2(), 0.5); + long outputU = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(1).getDim1(), + getOutputs().get(1).getDim2(), 0.5); + return outputL + outputU + outputP; + } + else if(getFunctionName().equalsIgnoreCase("eigen")) { + long outputVectors = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(0).getDim1(), + getOutputs().get(0).getDim2(), 1.0); + long outputValues = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(1).getDim1(), 1, 1.0); + return outputVectors + outputValues; + } + else if(getFunctionName().equalsIgnoreCase("fft")) { long outputRe = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(0).getDim1(), getOutputs().get(0).getDim2(), 1.0); long outputIm = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(1).getDim1(), @@ -274,165 +218,241 @@ else if(getFunctionName().equalsIgnoreCase("ifft")) { getOutputs().get(1).getDim2(), 1.0); return outputRe + outputIm; } - else if (getFunctionName().equalsIgnoreCase("batch_norm2d") || getFunctionName().equalsIgnoreCase("batch_norm2d_backward") || - getFunctionName().equalsIgnoreCase("batch_norm2d_train") || getFunctionName().equalsIgnoreCase("batch_norm2d_test")) { - return 0; - } - else if ( getFunctionName().equalsIgnoreCase("lstm") || getFunctionName().equalsIgnoreCase("lstm_backward") ) { - // TODO: To allow for initial version to always run on the GPU - return 0; - } - else if ( getFunctionName().equalsIgnoreCase("svd")) { - double interOutput = OptimizerUtils.estimateSizeExactSparsity(1, getInput().get(0).getDim2(), 1.0); - return interOutput; - } - else - throw new RuntimeException("Invalid call of computeIntermediateMemEstimate in FunctionOp."); - } - } - - @Override - protected DataCharacteristics inferOutputCharacteristics( MemoTable memo ) { - throw new RuntimeException("Invalid call of inferOutputCharacteristics in FunctionOp."); - } - - @Override - public boolean isGPUEnabled() { - if(getFunctionName().equalsIgnoreCase("lstm") || getFunctionName().equalsIgnoreCase("lstm_backward") || - getFunctionName().equalsIgnoreCase("batch_norm2d") || getFunctionName().equalsIgnoreCase("batch_norm2d_backward") || - getFunctionName().equalsIgnoreCase("batch_norm2d_train") || getFunctionName().equalsIgnoreCase("batch_norm2d_test")) - return true; - else - return false; - } - - @Override - public Lop constructLops() - { - //return already created lops - if( getLops() != null ) - return getLops(); - - ExecType et = optFindExecType(); - - //construct input lops (recursive) - ArrayList tmp = new ArrayList<>(); - for( Hop in : getInput() ) - tmp.add( in.constructLops() ); - - //construct function call - FunctionCallCP fcall = new FunctionCallCP(tmp, _fnamespace, _fname, _inputNames, _outputNames, _outputHops, _opt, et); - setLineNumbers(fcall); - setLops(fcall); - - //note: no reblock lop because outputs directly bound - constructAndSetCompressionLopFunctionalIfRequired(et); - - return getLops(); - } - - protected void constructAndSetCompressionLopFunctionalIfRequired(ExecType et) { - if((requiresCompression()) && ((FunctionCallCP) getLops()).getFunctionName().equalsIgnoreCase("transformencode")){ // xor - - // Lop matrixOut = lop.getFunctionOutputs().get(0); - Lop compressionInstruction = null; - - if(_compressedWorkloadTree != null) { - SingletonLookupHashMap m = SingletonLookupHashMap.getMap(); - int singletonID = m.put(_compressedWorkloadTree); - compressionInstruction = new Compression(getLops(), DataType.MATRIX, ValueType.FP64, et, singletonID); - } - else - compressionInstruction = new Compression(getLops(), DataType.MATRIX, ValueType.FP64, et, 0); - - - setOutputDimensions( compressionInstruction ); - setLineNumbers( compressionInstruction ); - setLops( compressionInstruction ); - - } - } - - - @Override - public String getOpString() { - return OPCODE + " " + _fnamespace + " " + _fname; - } - - @Override - protected ExecType optFindExecType(boolean transitive) - { - checkAndSetForcedPlatform(); - - if ( getFunctionType() == FunctionType.MULTIRETURN_BUILTIN ) { - boolean isBuiltinFunction = isBuiltinFunction(); - // check if there is sufficient memory to execute this function - if(isBuiltinFunction && getFunctionName().equalsIgnoreCase("transformencode") ) { - _etype = ((_etypeForced==ExecType.SPARK - || (getMemEstimate() >= OptimizerUtils.getLocalMemBudget() - && OptimizerUtils.isSparkExecutionMode())) ? ExecType.SPARK : ExecType.CP); - } - else if(isBuiltinFunction && (getFunctionName().equalsIgnoreCase("lstm") || getFunctionName().equalsIgnoreCase("lstm_backward"))) { - if(!DMLScript.USE_ACCELERATOR) - throw new RuntimeException("The function " + getFunctionName() + " is only supported on GPU."); - _etype = ExecType.GPU; - } - else if(isBuiltinFunction && (getFunctionName().equalsIgnoreCase("batch_norm2d") || getFunctionName().equalsIgnoreCase("batch_norm2d_backward"))) { - _etype = DMLScript.USE_ACCELERATOR ? ExecType.GPU : ExecType.CP; - } - else if(isBuiltinFunction && getFunctionName().equalsIgnoreCase("batch_norm2d_train")) { - // Only GPU implementation is supported - _etype = ExecType.GPU; - } - else { - // Since the memory estimate is only conservative, do not throw - // exception if the estimated memory is larger than the budget - // Nevertheless, memory estimates these functions are useful for - // other purposes, such as compiling parfor - _etype = ExecType.CP; - } - } - else { - // the actual function call is always CP - _etype = ExecType.CP; - } - - return _etype; - } - - private boolean isBuiltinFunction() { - return getFunctionNamespace().equals(DMLProgram.INTERNAL_NAMESPACE); - } - - @Override - public void refreshSizeInformation() { - //do nothing - } - - @Override - @SuppressWarnings("unchecked") - public Object clone() throws CloneNotSupportedException { - FunctionOp ret = new FunctionOp(); - - //copy generic attributes - ret.clone(this, false); - - //copy specific attributes - ret._type = _type; - ret._fnamespace = _fnamespace; - ret._fname = _fname; - ret._opt = _opt; - ret._inputNames = (_inputNames!=null) ? _inputNames.clone() : null; - ret._outputNames = _outputNames.clone(); - if( _outputHops != null ) - ret._outputHops = (ArrayList) _outputHops.clone(); - - return ret; - } - - @Override - public boolean compare(Hop that) { - return false; - } - - } \ No newline at end of file + else if(getFunctionName().equalsIgnoreCase("lstm") || getFunctionName().equalsIgnoreCase("lstm_backward")) { + // TODO: To allow for initial version to always run on the GPU + return 0; + } + else if(getFunctionName().equalsIgnoreCase("batch_norm2d") || + getFunctionName().equalsIgnoreCase("batch_norm2d_train")) { + return OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(0).getDim1(), + getOutputs().get(0).getDim2(), 1.0) + + OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(1).getDim1(), + getOutputs().get(1).getDim2(), 1.0) + + OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(2).getDim1(), + getOutputs().get(2).getDim2(), 1.0) + + OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(3).getDim1(), + getOutputs().get(3).getDim2(), 1.0) + + OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(4).getDim1(), + getOutputs().get(4).getDim2(), 1.0); + } + else if(getFunctionName().equalsIgnoreCase("batch_norm2d_test")) { + return OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(0).getDim1(), + getOutputs().get(0).getDim2(), 1.0); + } + else if(getFunctionName().equalsIgnoreCase("batch_norm2d_backward")) { + return OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(0).getDim1(), + getOutputs().get(0).getDim2(), 1.0) + + OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(1).getDim1(), + getOutputs().get(1).getDim2(), 1.0) + + OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(2).getDim1(), + getOutputs().get(2).getDim2(), 1.0); + } + else if(getFunctionName().equalsIgnoreCase("svd")) { + long outputU = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(0).getDim1(), + getOutputs().get(0).getDim2(), 1.0); + long outputSigma = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(1).getDim1(), + getOutputs().get(1).getDim2(), 1.0); + long outputV = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(2).getDim1(), + getOutputs().get(2).getDim2(), 1.0); + return outputU + outputSigma + outputV; + } + else + throw new RuntimeException("Invalid call of computeOutputMemEstimate in FunctionOp."); + } + } + + @Override + protected double computeIntermediateMemEstimate(long dim1, long dim2, long nnz) { + if(getFunctionType() != FunctionType.MULTIRETURN_BUILTIN) + throw new RuntimeException("Invalid call of computeIntermediateMemEstimate in FunctionOp."); + else { + if(getFunctionName().equalsIgnoreCase("qr")) { + // matrix of size same as the input + return OptimizerUtils.estimateSizeExactSparsity(getInput().get(0).getDim1(), + getInput().get(0).getDim2(), 1.0); + } + else if(getFunctionName().equalsIgnoreCase("lu")) { + // 1D vector + return OptimizerUtils.estimateSizeExactSparsity(getInput().get(0).getDim1(), 1, 1.0); + } + else if(getFunctionName().equalsIgnoreCase("eigen")) { + // One matrix of size original input and three 1D vectors (used to represent tridiagonal matrix) + return OptimizerUtils.estimateSizeExactSparsity(getInput().get(0).getDim1(), + getInput().get(0).getDim2(), 1.0) + + 3 * OptimizerUtils.estimateSizeExactSparsity(getInput().get(0).getDim1(), 1, 1.0); + } + else if(getFunctionName().equalsIgnoreCase("fft")) { + // 2 matrices of size same as the input + return 2 * OptimizerUtils.estimateSizeExactSparsity(getInput().get(0).getDim1(), + getInput().get(0).getDim2(), 1.0); + } + else if(getFunctionName().equalsIgnoreCase("ifft")) { + // 2 matrices of size same as the input + return 2 * OptimizerUtils.estimateSizeExactSparsity(getInput().get(0).getDim1(), + getInput().get(0).getDim2(), 1.0); + } + else if(getFunctionName().equalsIgnoreCase("batch_norm2d") || + getFunctionName().equalsIgnoreCase("batch_norm2d_backward") || + getFunctionName().equalsIgnoreCase("batch_norm2d_train") || + getFunctionName().equalsIgnoreCase("batch_norm2d_test")) { + return 0; + } + else if(getFunctionName().equalsIgnoreCase("lstm") || getFunctionName().equalsIgnoreCase("lstm_backward")) { + // TODO: To allow for initial version to always run on the GPU + return 0; + } + else if(getFunctionName().equalsIgnoreCase("svd")) { + double interOutput = OptimizerUtils.estimateSizeExactSparsity(1, getInput().get(0).getDim2(), 1.0); + return interOutput; + } + else + throw new RuntimeException("Invalid call of computeIntermediateMemEstimate in FunctionOp."); + } + } + + @Override + protected DataCharacteristics inferOutputCharacteristics(MemoTable memo) { + throw new RuntimeException("Invalid call of inferOutputCharacteristics in FunctionOp."); + } + + @Override + public boolean isGPUEnabled() { + if(getFunctionName().equalsIgnoreCase("lstm") || getFunctionName().equalsIgnoreCase("lstm_backward") || + getFunctionName().equalsIgnoreCase("batch_norm2d") || + getFunctionName().equalsIgnoreCase("batch_norm2d_backward") || + getFunctionName().equalsIgnoreCase("batch_norm2d_train") || + getFunctionName().equalsIgnoreCase("batch_norm2d_test")) + return true; + else + return false; + } + + @Override + public Lop constructLops() { + // return already created lops + if(getLops() != null) + return getLops(); + + ExecType et = optFindExecType(); + + // construct input lops (recursive) + ArrayList tmp = new ArrayList<>(); + for(Hop in : getInput()) + tmp.add(in.constructLops()); + + // construct function call + FunctionCallCP fcall = new FunctionCallCP(tmp, _fnamespace, _fname, _inputNames, _outputNames, _outputHops, + _opt, et); + setLineNumbers(fcall); + setLops(fcall); + + // note: no reblock lop because outputs directly bound + constructAndSetCompressionLopFunctionalIfRequired(et); + + return getLops(); + } + + protected void constructAndSetCompressionLopFunctionalIfRequired(ExecType et) { + if((requiresCompression()) && + ((FunctionCallCP) getLops()).getFunctionName().equalsIgnoreCase("transformencode")) { // xor + + // Lop matrixOut = lop.getFunctionOutputs().get(0); + Lop compressionInstruction = null; + + if(_compressedWorkloadTree != null) { + SingletonLookupHashMap m = SingletonLookupHashMap.getMap(); + int singletonID = m.put(_compressedWorkloadTree); + compressionInstruction = new Compression(getLops(), DataType.MATRIX, ValueType.FP64, et, singletonID); + } + else + compressionInstruction = new Compression(getLops(), DataType.MATRIX, ValueType.FP64, et, 0); + + setOutputDimensions(compressionInstruction); + setLineNumbers(compressionInstruction); + setLops(compressionInstruction); + + } + } + + @Override + public String getOpString() { + return OPCODE + " " + _fnamespace + " " + _fname; + } + + @Override + protected ExecType optFindExecType(boolean transitive) { + checkAndSetForcedPlatform(); + + if(getFunctionType() == FunctionType.MULTIRETURN_BUILTIN) { + boolean isBuiltinFunction = isBuiltinFunction(); + // check if there is sufficient memory to execute this function + if(isBuiltinFunction && getFunctionName().equalsIgnoreCase("transformencode")) { + _etype = ((_etypeForced == ExecType.SPARK || (getMemEstimate() >= OptimizerUtils.getLocalMemBudget() && + OptimizerUtils.isSparkExecutionMode())) ? ExecType.SPARK : ExecType.CP); + } + else if(isBuiltinFunction && + (getFunctionName().equalsIgnoreCase("lstm") || getFunctionName().equalsIgnoreCase("lstm_backward"))) { + if(!DMLScript.USE_ACCELERATOR) + throw new RuntimeException("The function " + getFunctionName() + " is only supported on GPU."); + _etype = ExecType.GPU; + } + else if(isBuiltinFunction && (getFunctionName().equalsIgnoreCase("batch_norm2d") || + getFunctionName().equalsIgnoreCase("batch_norm2d_backward"))) { + _etype = DMLScript.USE_ACCELERATOR ? ExecType.GPU : ExecType.CP; + } + else if(isBuiltinFunction && getFunctionName().equalsIgnoreCase("batch_norm2d_train")) { + // Only GPU implementation is supported + _etype = ExecType.GPU; + } + else { + // Since the memory estimate is only conservative, do not throw + // exception if the estimated memory is larger than the budget + // Nevertheless, memory estimates these functions are useful for + // other purposes, such as compiling parfor + _etype = ExecType.CP; + } + } + else { + // the actual function call is always CP + _etype = ExecType.CP; + } + + return _etype; + } + + private boolean isBuiltinFunction() { + return getFunctionNamespace().equals(DMLProgram.INTERNAL_NAMESPACE); + } + + @Override + public void refreshSizeInformation() { + // do nothing + } + + @Override + @SuppressWarnings("unchecked") + public Object clone() throws CloneNotSupportedException { + FunctionOp ret = new FunctionOp(); + + // copy generic attributes + ret.clone(this, false); + + // copy specific attributes + ret._type = _type; + ret._fnamespace = _fnamespace; + ret._fname = _fname; + ret._opt = _opt; + ret._inputNames = (_inputNames != null) ? _inputNames.clone() : null; + ret._outputNames = _outputNames.clone(); + if(_outputHops != null) + ret._outputHops = (ArrayList) _outputHops.clone(); + + return ret; + } + + @Override + public boolean compare(Hop that) { + return false; + } + +} From e6085f95588f20e7d8279e894e9076f658c67904 Mon Sep 17 00:00:00 2001 From: Frederic Zoepffel Date: Thu, 8 Feb 2024 20:56:21 +0100 Subject: [PATCH 052/133] Revert "formatting" This reverts commit 9321a32c817d4f99dd9189ce86e625987c2367c5. --- .../org/apache/sysds/hops/FunctionOp.java | 376 ++- .../parser/BuiltinFunctionExpression.java | 952 +++---- .../apache/sysds/parser/DMLTranslator.java | 2508 ++++++++--------- .../instructions/CPInstructionParser.java | 16 +- .../instructions/cp/CPInstruction.java | 49 +- .../cp/MultiReturnBuiltinCPInstruction.java | 25 +- ...turnComplexMatrixBuiltinCPInstruction.java | 225 +- .../runtime/matrix/data/LibCommonsMath.java | 210 +- .../runtime/matrix/data/LibMatrixFourier.java | 123 +- .../propagation/PrivacyPropagator.java | 332 ++- .../test/component/matrix/FourierTest.java | 251 +- 11 files changed, 2528 insertions(+), 2539 deletions(-) diff --git a/src/main/java/org/apache/sysds/hops/FunctionOp.java b/src/main/java/org/apache/sysds/hops/FunctionOp.java index 26b9cc353d2..4d439bf1001 100644 --- a/src/main/java/org/apache/sysds/hops/FunctionOp.java +++ b/src/main/java/org/apache/sysds/hops/FunctionOp.java @@ -39,53 +39,53 @@ /** * This FunctionOp represents the call to a DML-bodied or external function. * - * Note: Currently, we support expressions in function arguments along with function calls in expressions with single - * outputs, leaving multiple outputs handling as it is. + * Note: Currently, we support expressions in function arguments along with function calls + * in expressions with single outputs, leaving multiple outputs handling as it is. */ -public class FunctionOp extends Hop { - public enum FunctionType { - DML, MULTIRETURN_BUILTIN, UNKNOWN - } - +public class FunctionOp extends Hop +{ + public enum FunctionType{ + DML, + MULTIRETURN_BUILTIN, + UNKNOWN + } + public static final String OPCODE = "fcall"; - + private FunctionType _type = null; private String _fnamespace = null; private String _fname = null; - private boolean _opt = true; // call to optimized/unoptimized + private boolean _opt = true; //call to optimized/unoptimized private boolean _pseudo = false; - - private String[] _inputNames = null; // A,B in C = foo(A=X, B=Y) + + private String[] _inputNames = null; // A,B in C = foo(A=X, B=Y) private String[] _outputNames = null; // C in C = foo(A=X, B=Y) private ArrayList _outputHops = null; - + private FunctionOp() { - // default constructor for clone + //default constructor for clone } - public FunctionOp(FunctionType type, String fnamespace, String fname, String[] inputNames, List inputs, - String[] outputNames, ArrayList outputHops) { + public FunctionOp(FunctionType type, String fnamespace, String fname, String[] inputNames, List inputs, String[] outputNames, ArrayList outputHops) { this(type, fnamespace, fname, inputNames, inputs, outputNames, false); _outputHops = outputHops; } - - public FunctionOp(FunctionType type, String fnamespace, String fname, String[] inputNames, List inputs, - String[] outputNames, boolean singleOut) { + public FunctionOp(FunctionType type, String fnamespace, String fname, String[] inputNames, List inputs, String[] outputNames, boolean singleOut) { this(type, fnamespace, fname, inputNames, inputs, outputNames, singleOut, false); } - - public FunctionOp(FunctionType type, String fnamespace, String fname, String[] inputNames, List inputs, - String[] outputNames, boolean singleOut, boolean pseudo) { - super(fnamespace + Program.KEY_DELIM + fname, DataType.UNKNOWN, ValueType.UNKNOWN); - + + public FunctionOp(FunctionType type, String fnamespace, String fname, String[] inputNames, List inputs, String[] outputNames, boolean singleOut, boolean pseudo) + { + super(fnamespace + Program.KEY_DELIM + fname, DataType.UNKNOWN, ValueType.UNKNOWN ); + _type = type; _fnamespace = fnamespace; _fname = fname; _inputNames = inputNames; _outputNames = outputNames; _pseudo = pseudo; - - for(Hop in : inputs) { + + for( Hop in : inputs ) { getInput().add(in); in.getParent().add(this); } @@ -93,57 +93,58 @@ public FunctionOp(FunctionType type, String fnamespace, String fname, String[] i /** FunctionOps may have any number of inputs. */ @Override - public void checkArity() { - } - + public void checkArity() {} + public String getFunctionKey() { - return DMLProgram.constructFunctionKey(getFunctionNamespace(), getFunctionName()); + return DMLProgram.constructFunctionKey( + getFunctionNamespace(), getFunctionName()); } - + public String getFunctionNamespace() { return _fnamespace; } - + public String getFunctionName() { return _fname; } - - public void setFunctionName(String fname) { + + public void setFunctionName( String fname ) { _fname = fname; } - - public void setFunctionNamespace(String fnamespace) { + + public void setFunctionNamespace( String fnamespace ) { _fnamespace = fnamespace; } - + public void setInputVariableNames(String[] names) { _inputNames = names; } - + public ArrayList getOutputs() { return _outputHops; } - + public String[] getInputVariableNames() { return _inputNames; } - + public String[] getOutputVariableNames() { return _outputNames; } - + public boolean containsOutput(String varname) { - return Arrays.stream(getOutputVariableNames()).anyMatch(outName -> outName.equals(varname)); + return Arrays.stream(getOutputVariableNames()) + .anyMatch(outName -> outName.equals(varname)); } - + public FunctionType getFunctionType() { return _type; } - + public void setCallOptimized(boolean opt) { _opt = opt; } - + public boolean isPseudoFunctionCall() { return _pseudo; } @@ -154,154 +155,128 @@ public boolean allowsAllExecTypes() { } @Override - public void computeMemEstimate(MemoTable memo) { - // overwrites default hops behavior - - if(_type == FunctionType.DML) - _memEstimate = 1; // minimal mem estimate - else if(_type == FunctionType.UNKNOWN) + public void computeMemEstimate( MemoTable memo ) + { + //overwrites default hops behavior + + if( _type == FunctionType.DML ) + _memEstimate = 1; //minimal mem estimate + else if( _type == FunctionType.UNKNOWN ) _memEstimate = CostEstimatorHops.DEFAULT_MEM_SP; - else if(_type == FunctionType.MULTIRETURN_BUILTIN) { + else if ( _type == FunctionType.MULTIRETURN_BUILTIN ) { boolean outputDimsKnown = true; - for(Hop out : getOutputs()) { + for(Hop out : getOutputs()){ outputDimsKnown &= out.dimsKnown(); } - if(outputDimsKnown) { - long lnnz = (getNnz() >= 0) ? getNnz() : getLength(); + if( outputDimsKnown ) { + long lnnz = (getNnz()>=0)?getNnz():getLength(); _outputMemEstimate = computeOutputMemEstimate(getDim1(), getDim2(), lnnz); _processingMemEstimate = computeIntermediateMemEstimate(getDim1(), getDim2(), lnnz); } _memEstimate = getInputOutputSize(); } } - + @Override - protected double computeOutputMemEstimate(long dim1, long dim2, long nnz) { - if(getFunctionType() != FunctionType.MULTIRETURN_BUILTIN) + protected double computeOutputMemEstimate( long dim1, long dim2, long nnz ) + { + if ( getFunctionType() != FunctionType.MULTIRETURN_BUILTIN ) throw new RuntimeException("Invalid call of computeOutputMemEstimate in FunctionOp."); else { - if(getFunctionName().equalsIgnoreCase("qr")) { + if ( getFunctionName().equalsIgnoreCase("qr") ) { // upper-triangular and lower-triangular matrices - long outputH = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(0).getDim1(), - getOutputs().get(0).getDim2(), 0.5); - long outputR = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(1).getDim1(), - getOutputs().get(1).getDim2(), 0.5); - return outputH + outputR; + long outputH = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(0).getDim1(), getOutputs().get(0).getDim2(), 0.5); + long outputR = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(1).getDim1(), getOutputs().get(1).getDim2(), 0.5); + return outputH+outputR; } - else if(getFunctionName().equalsIgnoreCase("lu")) { + else if ( getFunctionName().equalsIgnoreCase("lu") ) { // upper-triangular and lower-triangular matrices - long outputP = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(1).getDim1(), - getOutputs().get(1).getDim2(), 1.0 / getOutputs().get(1).getDim2()); - long outputL = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(0).getDim1(), - getOutputs().get(0).getDim2(), 0.5); - long outputU = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(1).getDim1(), - getOutputs().get(1).getDim2(), 0.5); - return outputL + outputU + outputP; + long outputP = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(1).getDim1(), getOutputs().get(1).getDim2(), 1.0/getOutputs().get(1).getDim2()); + long outputL = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(0).getDim1(), getOutputs().get(0).getDim2(), 0.5); + long outputU = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(1).getDim1(), getOutputs().get(1).getDim2(), 0.5); + return outputL+outputU+outputP; } - else if(getFunctionName().equalsIgnoreCase("eigen")) { - long outputVectors = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(0).getDim1(), - getOutputs().get(0).getDim2(), 1.0); + else if ( getFunctionName().equalsIgnoreCase("eigen") ) { + long outputVectors = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(0).getDim1(), getOutputs().get(0).getDim2(), 1.0); long outputValues = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(1).getDim1(), 1, 1.0); - return outputVectors + outputValues; + return outputVectors+outputValues; } - else if(getFunctionName().equalsIgnoreCase("fft")) { - long outputRe = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(0).getDim1(), - getOutputs().get(0).getDim2(), 1.0); - long outputIm = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(1).getDim1(), - getOutputs().get(1).getDim2(), 1.0); - return outputRe + outputIm; + else if ( getFunctionName().equalsIgnoreCase("fft") ) { + long outputRe = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(0).getDim1(), getOutputs().get(0).getDim2(), 1.0); + long outputIm = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(1).getDim1(), getOutputs().get(1).getDim2(), 1.0); + return outputRe+outputIm; } - else if(getFunctionName().equalsIgnoreCase("ifft")) { - long outputRe = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(0).getDim1(), - getOutputs().get(0).getDim2(), 1.0); - long outputIm = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(1).getDim1(), - getOutputs().get(1).getDim2(), 1.0); - return outputRe + outputIm; + else if ( getFunctionName().equalsIgnoreCase("ifft") ) { + long outputRe = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(0).getDim1(), getOutputs().get(0).getDim2(), 1.0); + long outputIm = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(1).getDim1(), getOutputs().get(1).getDim2(), 1.0); + return outputRe+outputIm; } - else if(getFunctionName().equalsIgnoreCase("lstm") || getFunctionName().equalsIgnoreCase("lstm_backward")) { + else if ( getFunctionName().equalsIgnoreCase("lstm") || getFunctionName().equalsIgnoreCase("lstm_backward") ) { // TODO: To allow for initial version to always run on the GPU - return 0; - } - else if(getFunctionName().equalsIgnoreCase("batch_norm2d") || - getFunctionName().equalsIgnoreCase("batch_norm2d_train")) { - return OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(0).getDim1(), - getOutputs().get(0).getDim2(), 1.0) + - OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(1).getDim1(), - getOutputs().get(1).getDim2(), 1.0) + - OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(2).getDim1(), - getOutputs().get(2).getDim2(), 1.0) + - OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(3).getDim1(), - getOutputs().get(3).getDim2(), 1.0) + - OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(4).getDim1(), - getOutputs().get(4).getDim2(), 1.0); + return 0; } - else if(getFunctionName().equalsIgnoreCase("batch_norm2d_test")) { - return OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(0).getDim1(), - getOutputs().get(0).getDim2(), 1.0); + else if ( getFunctionName().equalsIgnoreCase("batch_norm2d") || getFunctionName().equalsIgnoreCase("batch_norm2d_train")) { + return OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(0).getDim1(), getOutputs().get(0).getDim2(), 1.0) + + OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(1).getDim1(), getOutputs().get(1).getDim2(), 1.0) + + OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(2).getDim1(), getOutputs().get(2).getDim2(), 1.0) + + OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(3).getDim1(), getOutputs().get(3).getDim2(), 1.0) + + OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(4).getDim1(), getOutputs().get(4).getDim2(), 1.0); } - else if(getFunctionName().equalsIgnoreCase("batch_norm2d_backward")) { - return OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(0).getDim1(), - getOutputs().get(0).getDim2(), 1.0) + - OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(1).getDim1(), - getOutputs().get(1).getDim2(), 1.0) + - OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(2).getDim1(), - getOutputs().get(2).getDim2(), 1.0); + else if ( getFunctionName().equalsIgnoreCase("batch_norm2d_test") ) { + return OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(0).getDim1(), getOutputs().get(0).getDim2(), 1.0); + } + else if ( getFunctionName().equalsIgnoreCase("batch_norm2d_backward") ) { + return OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(0).getDim1(), getOutputs().get(0).getDim2(), 1.0) + + OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(1).getDim1(), getOutputs().get(1).getDim2(), 1.0) + + OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(2).getDim1(), getOutputs().get(2).getDim2(), 1.0); } - else if(getFunctionName().equalsIgnoreCase("svd")) { - long outputU = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(0).getDim1(), - getOutputs().get(0).getDim2(), 1.0); - long outputSigma = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(1).getDim1(), - getOutputs().get(1).getDim2(), 1.0); - long outputV = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(2).getDim1(), - getOutputs().get(2).getDim2(), 1.0); - return outputU + outputSigma + outputV; + else if ( getFunctionName().equalsIgnoreCase("svd") ) { + long outputU = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(0).getDim1(), getOutputs().get(0).getDim2(), 1.0); + long outputSigma = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(1).getDim1(), getOutputs().get(1).getDim2(), 1.0); + long outputV = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(2).getDim1(), getOutputs().get(2).getDim2(), 1.0); + return outputU+outputSigma+outputV; } else throw new RuntimeException("Invalid call of computeOutputMemEstimate in FunctionOp."); } } - + @Override - protected double computeIntermediateMemEstimate(long dim1, long dim2, long nnz) { - if(getFunctionType() != FunctionType.MULTIRETURN_BUILTIN) + protected double computeIntermediateMemEstimate( long dim1, long dim2, long nnz ) + { + if ( getFunctionType() != FunctionType.MULTIRETURN_BUILTIN ) throw new RuntimeException("Invalid call of computeIntermediateMemEstimate in FunctionOp."); else { - if(getFunctionName().equalsIgnoreCase("qr")) { + if ( getFunctionName().equalsIgnoreCase("qr") ) { // matrix of size same as the input - return OptimizerUtils.estimateSizeExactSparsity(getInput().get(0).getDim1(), - getInput().get(0).getDim2(), 1.0); + return OptimizerUtils.estimateSizeExactSparsity(getInput().get(0).getDim1(), getInput().get(0).getDim2(), 1.0); } - else if(getFunctionName().equalsIgnoreCase("lu")) { - // 1D vector - return OptimizerUtils.estimateSizeExactSparsity(getInput().get(0).getDim1(), 1, 1.0); + else if ( getFunctionName().equalsIgnoreCase("lu")) { + // 1D vector + return OptimizerUtils.estimateSizeExactSparsity(getInput().get(0).getDim1(), 1, 1.0); } - else if(getFunctionName().equalsIgnoreCase("eigen")) { + else if ( getFunctionName().equalsIgnoreCase("eigen")) { // One matrix of size original input and three 1D vectors (used to represent tridiagonal matrix) - return OptimizerUtils.estimateSizeExactSparsity(getInput().get(0).getDim1(), - getInput().get(0).getDim2(), 1.0) + - 3 * OptimizerUtils.estimateSizeExactSparsity(getInput().get(0).getDim1(), 1, 1.0); + return OptimizerUtils.estimateSizeExactSparsity(getInput().get(0).getDim1(), getInput().get(0).getDim2(), 1.0) + + 3*OptimizerUtils.estimateSizeExactSparsity(getInput().get(0).getDim1(), 1, 1.0); } - else if(getFunctionName().equalsIgnoreCase("fft")) { + else if ( getFunctionName().equalsIgnoreCase("fft") ) { // 2 matrices of size same as the input - return 2 * OptimizerUtils.estimateSizeExactSparsity(getInput().get(0).getDim1(), - getInput().get(0).getDim2(), 1.0); + return 2*OptimizerUtils.estimateSizeExactSparsity(getInput().get(0).getDim1(), getInput().get(0).getDim2(), 1.0); } - else if(getFunctionName().equalsIgnoreCase("ifft")) { + else if ( getFunctionName().equalsIgnoreCase("ifft") ) { // 2 matrices of size same as the input - return 2 * OptimizerUtils.estimateSizeExactSparsity(getInput().get(0).getDim1(), - getInput().get(0).getDim2(), 1.0); + return 2*OptimizerUtils.estimateSizeExactSparsity(getInput().get(0).getDim1(), getInput().get(0).getDim2(), 1.0); } - else if(getFunctionName().equalsIgnoreCase("batch_norm2d") || - getFunctionName().equalsIgnoreCase("batch_norm2d_backward") || - getFunctionName().equalsIgnoreCase("batch_norm2d_train") || - getFunctionName().equalsIgnoreCase("batch_norm2d_test")) { - return 0; + else if (getFunctionName().equalsIgnoreCase("batch_norm2d") || getFunctionName().equalsIgnoreCase("batch_norm2d_backward") || + getFunctionName().equalsIgnoreCase("batch_norm2d_train") || getFunctionName().equalsIgnoreCase("batch_norm2d_test")) { + return 0; } - else if(getFunctionName().equalsIgnoreCase("lstm") || getFunctionName().equalsIgnoreCase("lstm_backward")) { + else if ( getFunctionName().equalsIgnoreCase("lstm") || getFunctionName().equalsIgnoreCase("lstm_backward") ) { // TODO: To allow for initial version to always run on the GPU - return 0; + return 0; } - else if(getFunctionName().equalsIgnoreCase("svd")) { + else if ( getFunctionName().equalsIgnoreCase("svd")) { double interOutput = OptimizerUtils.estimateSizeExactSparsity(1, getInput().get(0).getDim2(), 1.0); return interOutput; } @@ -309,56 +284,53 @@ else if(getFunctionName().equalsIgnoreCase("svd")) { throw new RuntimeException("Invalid call of computeIntermediateMemEstimate in FunctionOp."); } } - + @Override - protected DataCharacteristics inferOutputCharacteristics(MemoTable memo) { + protected DataCharacteristics inferOutputCharacteristics( MemoTable memo ) { throw new RuntimeException("Invalid call of inferOutputCharacteristics in FunctionOp."); } - + @Override public boolean isGPUEnabled() { - if(getFunctionName().equalsIgnoreCase("lstm") || getFunctionName().equalsIgnoreCase("lstm_backward") || - getFunctionName().equalsIgnoreCase("batch_norm2d") || - getFunctionName().equalsIgnoreCase("batch_norm2d_backward") || - getFunctionName().equalsIgnoreCase("batch_norm2d_train") || - getFunctionName().equalsIgnoreCase("batch_norm2d_test")) + if(getFunctionName().equalsIgnoreCase("lstm") || getFunctionName().equalsIgnoreCase("lstm_backward") || + getFunctionName().equalsIgnoreCase("batch_norm2d") || getFunctionName().equalsIgnoreCase("batch_norm2d_backward") || + getFunctionName().equalsIgnoreCase("batch_norm2d_train") || getFunctionName().equalsIgnoreCase("batch_norm2d_test")) return true; else return false; } - + @Override - public Lop constructLops() { - // return already created lops - if(getLops() != null) + public Lop constructLops() + { + //return already created lops + if( getLops() != null ) return getLops(); - + ExecType et = optFindExecType(); - - // construct input lops (recursive) + + //construct input lops (recursive) ArrayList tmp = new ArrayList<>(); - for(Hop in : getInput()) - tmp.add(in.constructLops()); - - // construct function call - FunctionCallCP fcall = new FunctionCallCP(tmp, _fnamespace, _fname, _inputNames, _outputNames, _outputHops, - _opt, et); + for( Hop in : getInput() ) + tmp.add( in.constructLops() ); + + //construct function call + FunctionCallCP fcall = new FunctionCallCP(tmp, _fnamespace, _fname, _inputNames, _outputNames, _outputHops, _opt, et); setLineNumbers(fcall); setLops(fcall); - - // note: no reblock lop because outputs directly bound + + //note: no reblock lop because outputs directly bound constructAndSetCompressionLopFunctionalIfRequired(et); return getLops(); } protected void constructAndSetCompressionLopFunctionalIfRequired(ExecType et) { - if((requiresCompression()) && - ((FunctionCallCP) getLops()).getFunctionName().equalsIgnoreCase("transformencode")) { // xor - + if((requiresCompression()) && ((FunctionCallCP) getLops()).getFunctionName().equalsIgnoreCase("transformencode")){ // xor + // Lop matrixOut = lop.getFunctionOutputs().get(0); Lop compressionInstruction = null; - + if(_compressedWorkloadTree != null) { SingletonLookupHashMap m = SingletonLookupHashMap.getMap(); int singletonID = m.put(_compressedWorkloadTree); @@ -366,38 +338,40 @@ protected void constructAndSetCompressionLopFunctionalIfRequired(ExecType et) { } else compressionInstruction = new Compression(getLops(), DataType.MATRIX, ValueType.FP64, et, 0); + - setOutputDimensions(compressionInstruction); - setLineNumbers(compressionInstruction); - setLops(compressionInstruction); + setOutputDimensions( compressionInstruction ); + setLineNumbers( compressionInstruction ); + setLops( compressionInstruction ); } } + @Override public String getOpString() { - return OPCODE + " " + _fnamespace + " " + _fname; + return OPCODE + " " + _fnamespace + " " + _fname; } @Override - protected ExecType optFindExecType(boolean transitive) { + protected ExecType optFindExecType(boolean transitive) + { checkAndSetForcedPlatform(); - - if(getFunctionType() == FunctionType.MULTIRETURN_BUILTIN) { + + if ( getFunctionType() == FunctionType.MULTIRETURN_BUILTIN ) { boolean isBuiltinFunction = isBuiltinFunction(); // check if there is sufficient memory to execute this function - if(isBuiltinFunction && getFunctionName().equalsIgnoreCase("transformencode")) { - _etype = ((_etypeForced == ExecType.SPARK || (getMemEstimate() >= OptimizerUtils.getLocalMemBudget() && - OptimizerUtils.isSparkExecutionMode())) ? ExecType.SPARK : ExecType.CP); + if(isBuiltinFunction && getFunctionName().equalsIgnoreCase("transformencode") ) { + _etype = ((_etypeForced==ExecType.SPARK + || (getMemEstimate() >= OptimizerUtils.getLocalMemBudget() + && OptimizerUtils.isSparkExecutionMode())) ? ExecType.SPARK : ExecType.CP); } - else if(isBuiltinFunction && - (getFunctionName().equalsIgnoreCase("lstm") || getFunctionName().equalsIgnoreCase("lstm_backward"))) { + else if(isBuiltinFunction && (getFunctionName().equalsIgnoreCase("lstm") || getFunctionName().equalsIgnoreCase("lstm_backward"))) { if(!DMLScript.USE_ACCELERATOR) throw new RuntimeException("The function " + getFunctionName() + " is only supported on GPU."); _etype = ExecType.GPU; } - else if(isBuiltinFunction && (getFunctionName().equalsIgnoreCase("batch_norm2d") || - getFunctionName().equalsIgnoreCase("batch_norm2d_backward"))) { + else if(isBuiltinFunction && (getFunctionName().equalsIgnoreCase("batch_norm2d") || getFunctionName().equalsIgnoreCase("batch_norm2d_backward"))) { _etype = DMLScript.USE_ACCELERATOR ? ExecType.GPU : ExecType.CP; } else if(isBuiltinFunction && getFunctionName().equalsIgnoreCase("batch_norm2d_train")) { @@ -407,7 +381,7 @@ else if(isBuiltinFunction && getFunctionName().equalsIgnoreCase("batch_norm2d_tr else { // Since the memory estimate is only conservative, do not throw // exception if the estimated memory is larger than the budget - // Nevertheless, memory estimates these functions are useful for + // Nevertheless, memory estimates these functions are useful for // other purposes, such as compiling parfor _etype = ExecType.CP; } @@ -416,40 +390,40 @@ else if(isBuiltinFunction && getFunctionName().equalsIgnoreCase("batch_norm2d_tr // the actual function call is always CP _etype = ExecType.CP; } - + return _etype; } - + private boolean isBuiltinFunction() { return getFunctionNamespace().equals(DMLProgram.INTERNAL_NAMESPACE); } @Override public void refreshSizeInformation() { - // do nothing + //do nothing } - + @Override @SuppressWarnings("unchecked") public Object clone() throws CloneNotSupportedException { FunctionOp ret = new FunctionOp(); - - // copy generic attributes + + //copy generic attributes ret.clone(this, false); - - // copy specific attributes + + //copy specific attributes ret._type = _type; ret._fnamespace = _fnamespace; ret._fname = _fname; ret._opt = _opt; - ret._inputNames = (_inputNames != null) ? _inputNames.clone() : null; + ret._inputNames = (_inputNames!=null) ? _inputNames.clone() : null; ret._outputNames = _outputNames.clone(); - if(_outputHops != null) + if( _outputHops != null ) ret._outputHops = (ArrayList) _outputHops.clone(); - + return ret; } - + @Override public boolean compare(Hop that) { return false; diff --git a/src/main/java/org/apache/sysds/parser/BuiltinFunctionExpression.java b/src/main/java/org/apache/sysds/parser/BuiltinFunctionExpression.java index 2f6cf9c01f1..04f0d0fc0ac 100644 --- a/src/main/java/org/apache/sysds/parser/BuiltinFunctionExpression.java +++ b/src/main/java/org/apache/sysds/parser/BuiltinFunctionExpression.java @@ -45,12 +45,12 @@ public class BuiltinFunctionExpression extends DataIdentifier { private Builtins _opcode; public BuiltinFunctionExpression(ParserRuleContext ctx, Builtins bifop, ArrayList args, - String fname) { + String fname) { _opcode = bifop; setCtxValuesAndFilename(ctx, fname); args = expandDnnArguments(args); _args = new Expression[args.size()]; - for(int i = 0; i < args.size(); i++) { + for (int i = 0; i < args.size(); i++) { _args[i] = args.get(i).getExpr(); } } @@ -58,7 +58,7 @@ public BuiltinFunctionExpression(ParserRuleContext ctx, Builtins bifop, ArrayLis public BuiltinFunctionExpression(Builtins bifop, Expression[] args, ParseInfo parseInfo) { _opcode = bifop; _args = new Expression[args.length]; - for(int i = 0; i < args.length; i++) { + for (int i = 0; i < args.length; i++) { _args[i] = args[i]; } setParseInfo(parseInfo); @@ -67,7 +67,7 @@ public BuiltinFunctionExpression(Builtins bifop, Expression[] args, ParseInfo pa public BuiltinFunctionExpression(ParserRuleContext ctx, Builtins bifop, Expression[] args, String fname) { _opcode = bifop; _args = new Expression[args.length]; - for(int i = 0; i < args.length; i++) { + for (int i = 0; i < args.length; i++) { _args[i] = args[i]; } setCtxValuesAndFilename(ctx, fname); @@ -76,7 +76,7 @@ public BuiltinFunctionExpression(ParserRuleContext ctx, Builtins bifop, Expressi @Override public Expression rewriteExpression(String prefix) { Expression[] newArgs = new Expression[_args.length]; - for(int i = 0; i < _args.length; i++) { + for (int i = 0; i < _args.length; i++) { newArgs[i] = _args[i].rewriteExpression(prefix); } BuiltinFunctionExpression retVal = new BuiltinFunctionExpression(this._opcode, newArgs, this); @@ -88,35 +88,35 @@ public Builtins getOpCode() { } public Expression getFirstExpr() { - return(_args.length >= 1 ? _args[0] : null); + return (_args.length >= 1 ? _args[0] : null); } public Expression getSecondExpr() { - return(_args.length >= 2 ? _args[1] : null); + return (_args.length >= 2 ? _args[1] : null); } public Expression getThirdExpr() { - return(_args.length >= 3 ? _args[2] : null); + return (_args.length >= 3 ? _args[2] : null); } public Expression getFourthExpr() { - return(_args.length >= 4 ? _args[3] : null); + return (_args.length >= 4 ? _args[3] : null); } public Expression getFifthExpr() { - return(_args.length >= 5 ? _args[4] : null); + return (_args.length >= 5 ? _args[4] : null); } public Expression getSixthExpr() { - return(_args.length >= 6 ? _args[5] : null); + return (_args.length >= 6 ? _args[5] : null); } public Expression getSeventhExpr() { - return(_args.length >= 7 ? _args[6] : null); + return (_args.length >= 7 ? _args[6] : null); } public Expression getEighthExpr() { - return(_args.length >= 8 ? _args[7] : null); + return (_args.length >= 8 ? _args[7] : null); } public Expression[] getAllExpr() { @@ -124,21 +124,21 @@ public Expression[] getAllExpr() { } public Expression getExpr(int i) { - return(_args.length > i ? _args[i] : null); + return (_args.length > i ? _args[i] : null); } @Override public void validateExpression(MultiAssignmentStatement stmt, HashMap ids, - HashMap constVars, boolean conditional) { - if(this.getFirstExpr() instanceof FunctionCallIdentifier) { + HashMap constVars, boolean conditional) { + if (this.getFirstExpr() instanceof FunctionCallIdentifier) { raiseValidateError("UDF function call not supported as parameter to built-in function call", false); } this.getFirstExpr().validateExpression(ids, constVars, conditional); Expression[] expr = getAllExpr(); - if(expr != null && expr.length > 1) { - for(int i = 1; i < expr.length; i++) { - if(expr[i] instanceof FunctionCallIdentifier) { + if (expr != null && expr.length > 1) { + for (int i = 1; i < expr.length; i++) { + if (expr[i] instanceof FunctionCallIdentifier) { raiseValidateError("UDF function call not supported as parameter to built-in function call", false); } expr[i].validateExpression(ids, constVars, conditional); @@ -146,13 +146,13 @@ public void validateExpression(MultiAssignmentStatement stmt, HashMap orderDnnParams(ArrayList paramExpression, - int skip) { + int skip) { ArrayList newParams = new ArrayList<>(); - for(int i = 0; i < skip; i++) + for (int i = 0; i < skip; i++) newParams.add(paramExpression.get(i)); - String[] orderedParams = {"stride1", "stride2", "padding1", "padding2", "input_shape1", "input_shape2", - "input_shape3", "input_shape4", "filter_shape1", "filter_shape2", "filter_shape3", "filter_shape4"}; - for(int i = 0; i < orderedParams.length; i++) { + String[] orderedParams = { + "stride1", "stride2", "padding1", "padding2", + "input_shape1", "input_shape2", "input_shape3", "input_shape4", + "filter_shape1", "filter_shape2", "filter_shape3", "filter_shape4" + }; + for (int i = 0; i < orderedParams.length; i++) { boolean found = false; - for(ParameterExpression param : paramExpression) { - if(param.getName() != null && param.getName().equals(orderedParams[i])) { + for (ParameterExpression param : paramExpression) { + if (param.getName() != null && param.getName().equals(orderedParams[i])) { found = true; newParams.add(param); } } - if(!found) { + if (!found) { throw new LanguageException("Incorrect parameters. Expected " + orderedParams[i] + " to be expanded."); } } @@ -541,17 +543,16 @@ private static ArrayList orderDnnParams(ArrayList replaceListParams(ArrayList paramExpression, - String inputVarName, String outputVarName, int startIndex) { + String inputVarName, String outputVarName, int startIndex) { ArrayList newParamExpression = new ArrayList<>(); int i = startIndex; int j = 1; // Assumption: sequential ordering pool_size1, pool_size2 - for(ParameterExpression expr : paramExpression) { - if(expr.getName() != null && expr.getName().equals(inputVarName + j)) { + for (ParameterExpression expr : paramExpression) { + if (expr.getName() != null && expr.getName().equals(inputVarName + j)) { newParamExpression.add(new ParameterExpression(outputVarName + i, expr.getExpr())); i++; j++; - } - else { + } else { newParamExpression.add(expr); } } @@ -559,23 +560,21 @@ private static ArrayList replaceListParams(ArrayList expandListParams(ArrayList paramExpression, - HashSet paramsToExpand) { + HashSet paramsToExpand) { ArrayList newParamExpressions = new ArrayList<>(); - for(ParameterExpression expr : paramExpression) { - if(paramsToExpand.contains(expr.getName())) { - if(expr.getExpr() instanceof ExpressionList) { + for (ParameterExpression expr : paramExpression) { + if (paramsToExpand.contains(expr.getName())) { + if (expr.getExpr() instanceof ExpressionList) { int i = 1; - for(Expression e : ((ExpressionList) expr.getExpr()).getValue()) { + for (Expression e : ((ExpressionList) expr.getExpr()).getValue()) { newParamExpressions.add(new ParameterExpression(expr.getName() + i, e)); i++; } } - } - else if(expr.getExpr() instanceof ExpressionList) { - throw new LanguageException( - "The parameter " + expr.getName() + " cannot be list or is not supported for the given function"); - } - else { + } else if (expr.getExpr() instanceof ExpressionList) { + throw new LanguageException("The parameter " + expr.getName() + + " cannot be list or is not supported for the given function"); + } else { newParamExpressions.add(expr); } } @@ -584,8 +583,8 @@ else if(expr.getExpr() instanceof ExpressionList) { private ArrayList expandDnnArguments(ArrayList paramExpression) { try { - if(_opcode == Builtins.CONV2D || _opcode == Builtins.CONV2D_BACKWARD_FILTER || - _opcode == Builtins.CONV2D_BACKWARD_DATA) { + if (_opcode == Builtins.CONV2D || _opcode == Builtins.CONV2D_BACKWARD_FILTER + || _opcode == Builtins.CONV2D_BACKWARD_DATA) { HashSet expand = new HashSet<>(); expand.add("input_shape"); expand.add("filter_shape"); @@ -593,9 +592,8 @@ private ArrayList expandDnnArguments(ArrayList expand = new HashSet<>(); expand.add("input_shape"); expand.add("pool_size"); @@ -605,30 +603,31 @@ else if(_opcode == Builtins.MAX_POOL || _opcode == Builtins.AVG_POOL || paramExpression.add(new ParameterExpression("filter_shape1", new IntIdentifier(1, this))); paramExpression.add(new ParameterExpression("filter_shape2", new IntIdentifier(1, this))); paramExpression = replaceListParams(paramExpression, "pool_size", "filter_shape", 3); - if(_opcode == Builtins.MAX_POOL_BACKWARD || _opcode == Builtins.AVG_POOL_BACKWARD) + if (_opcode == Builtins.MAX_POOL_BACKWARD || _opcode == Builtins.AVG_POOL_BACKWARD) paramExpression = orderDnnParams(paramExpression, 2); else paramExpression = orderDnnParams(paramExpression, 1); } - } - catch(LanguageException e) { + } catch (LanguageException e) { throw new RuntimeException(e); } return paramExpression; } private boolean isValidNoArgumentFunction() { - return getOpCode() == Builtins.TIME || getOpCode() == Builtins.LIST; + return getOpCode() == Builtins.TIME + || getOpCode() == Builtins.LIST; } /** - * Validate parse tree : Process BuiltinFunction Expression in an assignment statement + * Validate parse tree : Process BuiltinFunction Expression in an assignment + * statement */ @Override public void validateExpression(HashMap ids, HashMap constVars, - boolean conditional) { - for(int i = 0; i < _args.length; i++) { - if(_args[i] instanceof FunctionCallIdentifier) { + boolean conditional) { + for (int i = 0; i < _args.length; i++) { + if (_args[i] instanceof FunctionCallIdentifier) { raiseValidateError("UDF function call not supported as parameter to built-in function call", false); } _args[i].validateExpression(ids, constVars, conditional); @@ -639,101 +638,101 @@ public void validateExpression(HashMap ids, HashMap ids, HashMap 2) + if (cumSP && id.getDim2() > 2) raiseValidateError("Cumsumprod only supported over two-column matrices", conditional); output.setDataType(DataType.MATRIX); @@ -796,28 +793,31 @@ else if(getAllExpr().length == 2) { // binary break; case CAST_AS_SCALAR: checkNumParameters(1); - checkDataTypeParam(getFirstExpr(), DataType.MATRIX, DataType.FRAME, DataType.LIST); - if((getFirstExpr().getOutput().getDim1() != -1 && getFirstExpr().getOutput().getDim1() != 1) || - (getFirstExpr().getOutput().getDim2() != -1 && getFirstExpr().getOutput().getDim2() != 1)) { + checkDataTypeParam(getFirstExpr(), + DataType.MATRIX, DataType.FRAME, DataType.LIST); + if ((getFirstExpr().getOutput().getDim1() != -1 && getFirstExpr().getOutput().getDim1() != 1) + || (getFirstExpr().getOutput().getDim2() != -1 && getFirstExpr().getOutput().getDim2() != 1)) { raiseValidateError( - "dimension mismatch while casting matrix to scalar: dim1: " - + getFirstExpr().getOutput().getDim1() + " dim2 " + getFirstExpr().getOutput().getDim2(), - conditional, LanguageErrorCodes.INVALID_PARAMETERS); + "dimension mismatch while casting matrix to scalar: dim1: " + + getFirstExpr().getOutput().getDim1() + + " dim2 " + getFirstExpr().getOutput().getDim2(), + conditional, LanguageErrorCodes.INVALID_PARAMETERS); } output.setDataType(DataType.SCALAR); output.setDimensions(0, 0); output.setBlocksize(0); - output.setValueType((id.getValueType() != ValueType.UNKNOWN || id.getDataType() == DataType.LIST) ? id - .getValueType() : ValueType.FP64); + output.setValueType((id.getValueType() != ValueType.UNKNOWN + || id.getDataType() == DataType.LIST) ? id.getValueType() : ValueType.FP64); break; case CAST_AS_MATRIX: checkNumParameters(1); - checkDataTypeParam(getFirstExpr(), DataType.SCALAR, DataType.FRAME, DataType.LIST); + checkDataTypeParam(getFirstExpr(), + DataType.SCALAR, DataType.FRAME, DataType.LIST); output.setDataType(DataType.MATRIX); output.setDimensions(id.getDim1(), id.getDim2()); - if(getFirstExpr().getOutput().getDataType() == DataType.SCALAR) + if (getFirstExpr().getOutput().getDataType() == DataType.SCALAR) output.setDimensions(1, 1); // correction scalars - if(getFirstExpr().getOutput().getDataType() == DataType.LIST) + if (getFirstExpr().getOutput().getDataType() == DataType.LIST) output.setDimensions(-1, -1); // correction list: arbitrary object output.setBlocksize(id.getBlocksize()); output.setValueType(ValueType.FP64); // matrices always in double @@ -843,10 +843,9 @@ else if(getAllExpr().length == 2) { // binary case CAST_AS_FRAME: // operation as.frame // overloaded to take either one argument or 2 where second is column names - if(getSecondExpr() == null) {// there is no column names + if (getSecondExpr() == null) {// there is no column names checkNumParameters(1); - } - else { // there is column names + } else { // there is column names checkNumParameters(2); checkDataTypeParam(getSecondExpr(), DataType.LIST); } @@ -854,9 +853,9 @@ else if(getAllExpr().length == 2) { // binary checkDataTypeParam(getFirstExpr(), DataType.SCALAR, DataType.MATRIX, DataType.LIST); output.setDataType(DataType.FRAME); output.setDimensions(id.getDim1(), id.getDim2()); - if(getFirstExpr().getOutput().getDataType() == DataType.SCALAR) + if (getFirstExpr().getOutput().getDataType() == DataType.SCALAR) output.setDimensions(1, 1); // correction scalars - if(getFirstExpr().getOutput().getDataType() == DataType.LIST) + if (getFirstExpr().getOutput().getDataType() == DataType.LIST) output.setDimensions(-1, -1); // correction list: arbitrary object output.setBlocksize(id.getBlocksize()); output.setValueType(id.getValueType()); @@ -900,7 +899,7 @@ else if(getAllExpr().length == 2) { // binary case CBIND: case RBIND: // scalar string append (string concatenation with \n) - if(getFirstExpr().getOutput().getDataType() == DataType.SCALAR) { + if (getFirstExpr().getOutput().getDataType() == DataType.SCALAR) { checkNumParameters(2); checkScalarParam(getFirstExpr()); checkScalarParam(getSecondExpr()); @@ -908,20 +907,19 @@ else if(getAllExpr().length == 2) { // binary checkValueTypeParam(getSecondExpr(), ValueType.STRING); } // append (rbind/cbind) all the elements of a list - else if(getAllExpr().length == 1) { + else if (getAllExpr().length == 1) { checkDataTypeParam(getFirstExpr(), DataType.LIST); - } - else { - if(getAllExpr().length < 2) + } else { + if (getAllExpr().length < 2) raiseValidateError("Invalid number of arguments for " + getOpCode(), conditional); // list append - if(getFirstExpr().getOutput().getDataType().isList()) - for(int i = 1; i < getAllExpr().length; i++) + if (getFirstExpr().getOutput().getDataType().isList()) + for (int i = 1; i < getAllExpr().length; i++) checkDataTypeParam(getExpr(i), DataType.SCALAR, DataType.MATRIX, DataType.FRAME, - DataType.LIST); + DataType.LIST); // matrix append (rbind/cbind) else - for(int i = 0; i < getAllExpr().length; i++) + for (int i = 0; i < getAllExpr().length; i++) checkMatrixFrameParam(getExpr(i)); } @@ -929,7 +927,7 @@ else if(getAllExpr().length == 1) { output.setValueType(id.getValueType()); // special handling of concatenating all list elements - if(id.getDataType() == DataType.LIST && getAllExpr().length == 1) { + if (id.getDataType() == DataType.LIST && getAllExpr().length == 1) { output.setDataType(DataType.MATRIX); output.setValueType(ValueType.FP64); } @@ -940,31 +938,28 @@ else if(getAllExpr().length == 1) { long appendDim1 = m1rlen, appendDim2 = m1clen; // best-effort dimension propagation and validation - if(id.getDataType() == DataType.LIST) { + if (id.getDataType() == DataType.LIST) { appendDim1 = -1; appendDim2 = -1; - } - else { - for(int i = 1; i < getAllExpr().length; i++) { + } else { + for (int i = 1; i < getAllExpr().length; i++) { long m2rlen = getExpr(i).getOutput().getDim1(); long m2clen = getExpr(i).getOutput().getDim2(); - if(getOpCode() == Builtins.CBIND) { - if(m1rlen >= 0 && m2rlen >= 0 && m1rlen != m2rlen) { - raiseValidateError( - "inputs to cbind must have same number of rows: input 1 rows: " + m1rlen - + ", input 2 rows: " + m2rlen, - conditional, LanguageErrorCodes.INVALID_PARAMETERS); + if (getOpCode() == Builtins.CBIND) { + if (m1rlen >= 0 && m2rlen >= 0 && m1rlen != m2rlen) { + raiseValidateError("inputs to cbind must have same number of rows: input 1 rows: " + + m1rlen + ", input 2 rows: " + m2rlen, conditional, + LanguageErrorCodes.INVALID_PARAMETERS); } appendDim1 = (m2rlen >= 0) ? m2rlen : appendDim1; appendDim2 = (appendDim2 >= 0 && m2clen >= 0) ? appendDim2 + m2clen : -1; - } - else if(getOpCode() == Builtins.RBIND) { - if(m1clen >= 0 && m2clen >= 0 && m1clen != m2clen) { + } else if (getOpCode() == Builtins.RBIND) { + if (m1clen >= 0 && m2clen >= 0 && m1clen != m2clen) { raiseValidateError( - "inputs to rbind must have same number of columns: input 1 columns: " + m1clen - + ", input 2 columns: " + m2clen, - conditional, LanguageErrorCodes.INVALID_PARAMETERS); + "inputs to rbind must have same number of columns: input 1 columns: " + + m1clen + ", input 2 columns: " + m2clen, + conditional, LanguageErrorCodes.INVALID_PARAMETERS); } appendDim1 = (appendDim1 >= 0 && m2rlen >= 0) ? appendDim1 + m2rlen : -1; appendDim2 = (m2clen >= 0) ? m2clen : appendDim2; @@ -988,20 +983,20 @@ else if(getOpCode() == Builtins.RBIND) { DataType dt2 = getSecondExpr().getOutput().getDataType(); // check input data types - if(dt1 == DataType.SCALAR && dt2 == DataType.SCALAR) { + if (dt1 == DataType.SCALAR && dt2 == DataType.SCALAR) { raiseValidateError("ppred() requires at least one matrix input.", conditional, - LanguageErrorCodes.INVALID_PARAMETERS); + LanguageErrorCodes.INVALID_PARAMETERS); } - if(dt1 == DataType.MATRIX) + if (dt1 == DataType.MATRIX) checkMatrixParam(getFirstExpr()); - if(dt2 == DataType.MATRIX) + if (dt2 == DataType.MATRIX) checkMatrixParam(getSecondExpr()); // check operator - if(getThirdExpr().getOutput().getDataType() != DataType.SCALAR || - getThirdExpr().getOutput().getValueType() != ValueType.STRING) { + if (getThirdExpr().getOutput().getDataType() != DataType.SCALAR || + getThirdExpr().getOutput().getValueType() != ValueType.STRING) { raiseValidateError("Third argument in ppred() is not an operator ", conditional, - LanguageErrorCodes.INVALID_PARAMETERS); + LanguageErrorCodes.INVALID_PARAMETERS); } setBinaryOutputProperties(output); @@ -1029,18 +1024,18 @@ else if(getOpCode() == Builtins.RBIND) { checkNumParameters(1); checkMatrixParam(getFirstExpr()); output.setDataType(DataType.MATRIX); - if(id.getDim2() != -1) { // type known - if(id.getDim2() == 1) { + if (id.getDim2() != -1) { // type known + if (id.getDim2() == 1) { // diag V2M output.setDimensions(id.getDim1(), id.getDim1()); - } - else { - if(id.getDim1() != id.getDim2()) { + } else { + if (id.getDim1() != id.getDim2()) { raiseValidateError( - "diag can either: (1) create diagonal matrix from (n x 1) matrix, or (2) take diagonal from a square matrix. " - + "Error invoking diag on matrix with dimensions (" + id.getDim1() + "," - + id.getDim2() + ") in " + this.toString(), - conditional, LanguageErrorCodes.INVALID_PARAMETERS); + "diag can either: (1) create diagonal matrix from (n x 1) matrix, or (2) take diagonal from a square matrix. " + + "Error invoking diag on matrix with dimensions (" + + id.getDim1() + "," + id.getDim2() + + ") in " + this.toString(), + conditional, LanguageErrorCodes.INVALID_PARAMETERS); } // diag M2V output.setDimensions(id.getDim1(), 1); @@ -1053,7 +1048,8 @@ else if(getOpCode() == Builtins.RBIND) { case NCOL: case LENGTH: checkNumParameters(1); - checkDataTypeParam(getFirstExpr(), DataType.FRAME, DataType.LIST, DataType.MATRIX); + checkDataTypeParam(getFirstExpr(), + DataType.FRAME, DataType.LIST, DataType.MATRIX); output.setDataType(DataType.SCALAR); output.setDimensions(0, 0); output.setBlocksize(0); @@ -1061,7 +1057,8 @@ else if(getOpCode() == Builtins.RBIND) { break; case LINEAGE: checkNumParameters(1); - checkDataTypeParam(getFirstExpr(), DataType.MATRIX, DataType.FRAME, DataType.LIST); + checkDataTypeParam(getFirstExpr(), + DataType.MATRIX, DataType.FRAME, DataType.LIST); output.setDataType(DataType.SCALAR); output.setDimensions(0, 0); output.setBlocksize(0); @@ -1086,8 +1083,14 @@ else if(getOpCode() == Builtins.RBIND) { case TABLE: /* - * Allowed #of arguments: 2,3,4,5,6 table(A,B) table(A,B,W) table(A,B,1) table(A,B,dim1,dim2) - * table(A,B,W,dim1,dim2) table(A,B,1,dim1,dim2) table(A,B,1,dim1,dim2,TRUE) + * Allowed #of arguments: 2,3,4,5,6 + * table(A,B) + * table(A,B,W) + * table(A,B,1) + * table(A,B,dim1,dim2) + * table(A,B,W,dim1,dim2) + * table(A,B,1,dim1,dim2) + * table(A,B,1,dim1,dim2,TRUE) */ // Check for validity of input arguments, and setup output dimensions @@ -1095,18 +1098,18 @@ else if(getOpCode() == Builtins.RBIND) { // First input: is always of type MATRIX checkMatrixParam(getFirstExpr()); - if(getSecondExpr() == null) + if (getSecondExpr() == null) raiseValidateError("Invalid number of arguments to table(). " - + "The table() function requires 2, 3, 4, 5, or 6 arguments.", conditional); + + "The table() function requires 2, 3, 4, 5, or 6 arguments.", conditional); // Second input: can be MATRIX or SCALAR // cases: table(A,B) or table(A,1) - if(getSecondExpr().getOutput().getDataType() == DataType.MATRIX) + if (getSecondExpr().getOutput().getDataType() == DataType.MATRIX) checkMatchingDimensions(getFirstExpr(), getSecondExpr()); long outputDim1 = -1, outputDim2 = -1; - switch(_args.length) { + switch (_args.length) { case 2: // nothing to do break; @@ -1115,7 +1118,7 @@ else if(getOpCode() == Builtins.RBIND) { // case - table w/ weights // - weights specified as a matrix: table(A,B,W) or table(A,1,W) // - weights specified as a scalar: table(A,B,1) or table(A,1,1) - if(getThirdExpr().getOutput().getDataType() == DataType.MATRIX) + if (getThirdExpr().getOutput().getDataType() == DataType.MATRIX) checkMatchingDimensions(getFirstExpr(), getThirdExpr()); break; @@ -1123,25 +1126,25 @@ else if(getOpCode() == Builtins.RBIND) { // case - table w/ output dimensions: table(A,B,dim1,dim2) or // table(A,1,dim1,dim2) // third and fourth arguments must be scalars - if(getThirdExpr().getOutput().getDataType() != DataType.SCALAR || - _args[3].getOutput().getDataType() != DataType.SCALAR) { + if (getThirdExpr().getOutput().getDataType() != DataType.SCALAR + || _args[3].getOutput().getDataType() != DataType.SCALAR) { raiseValidateError( - "Invalid argument types to table(): output dimensions must be of type scalar: " - + this.toString(), - conditional, LanguageErrorCodes.INVALID_PARAMETERS); - } - else { + "Invalid argument types to table(): output dimensions must be of type scalar: " + + this.toString(), + conditional, LanguageErrorCodes.INVALID_PARAMETERS); + } else { // constant propagation - if(getThirdExpr() instanceof DataIdentifier && - constVars.containsKey(((DataIdentifier) getThirdExpr()).getName()) && !conditional) + if (getThirdExpr() instanceof DataIdentifier + && constVars.containsKey(((DataIdentifier) getThirdExpr()).getName()) + && !conditional) _args[2] = constVars.get(((DataIdentifier) getThirdExpr()).getName()); - if(_args[3] instanceof DataIdentifier && - constVars.containsKey(((DataIdentifier) _args[3]).getName()) && !conditional) + if (_args[3] instanceof DataIdentifier + && constVars.containsKey(((DataIdentifier) _args[3]).getName()) && !conditional) _args[3] = constVars.get(((DataIdentifier) _args[3]).getName()); - if(getThirdExpr().getOutput() instanceof ConstIdentifier) + if (getThirdExpr().getOutput() instanceof ConstIdentifier) outputDim1 = ((ConstIdentifier) getThirdExpr().getOutput()).getLongValue(); - if(_args[3].getOutput() instanceof ConstIdentifier) + if (_args[3].getOutput() instanceof ConstIdentifier) outputDim2 = ((ConstIdentifier) _args[3].getOutput()).getLongValue(); } break; @@ -1152,42 +1155,41 @@ else if(getOpCode() == Builtins.RBIND) { // - table(A,B,W,dim1,dim2) or table(A,1,W,dim1,dim2) // - table(A,B,1,dim1,dim2) or table(A,1,1,dim1,dim2) - if(getThirdExpr().getOutput().getDataType() == DataType.MATRIX) + if (getThirdExpr().getOutput().getDataType() == DataType.MATRIX) checkMatchingDimensions(getFirstExpr(), getThirdExpr()); // fourth and fifth arguments must be scalars - if(_args[3].getOutput().getDataType() != DataType.SCALAR || - _args[4].getOutput().getDataType() != DataType.SCALAR) { + if (_args[3].getOutput().getDataType() != DataType.SCALAR + || _args[4].getOutput().getDataType() != DataType.SCALAR) { raiseValidateError( - "Invalid argument types to table(): output dimensions must be of type scalar: " - + this.toString(), - conditional, LanguageErrorCodes.INVALID_PARAMETERS); - } - else { + "Invalid argument types to table(): output dimensions must be of type scalar: " + + this.toString(), + conditional, LanguageErrorCodes.INVALID_PARAMETERS); + } else { // constant propagation - if(_args[3] instanceof DataIdentifier && - constVars.containsKey(((DataIdentifier) _args[3]).getName()) && !conditional) + if (_args[3] instanceof DataIdentifier + && constVars.containsKey(((DataIdentifier) _args[3]).getName()) && !conditional) _args[3] = constVars.get(((DataIdentifier) _args[3]).getName()); - if(_args[4] instanceof DataIdentifier && - constVars.containsKey(((DataIdentifier) _args[4]).getName()) && !conditional) + if (_args[4] instanceof DataIdentifier + && constVars.containsKey(((DataIdentifier) _args[4]).getName()) && !conditional) _args[4] = constVars.get(((DataIdentifier) _args[4]).getName()); - if(_args[3].getOutput() instanceof ConstIdentifier) + if (_args[3].getOutput() instanceof ConstIdentifier) outputDim1 = ((ConstIdentifier) _args[3].getOutput()).getLongValue(); - if(_args[4].getOutput() instanceof ConstIdentifier) + if (_args[4].getOutput() instanceof ConstIdentifier) outputDim2 = ((ConstIdentifier) _args[4].getOutput()).getLongValue(); } - if(_args.length == 6) { - if(!_args[5].getOutput().isScalarBoolean()) + if (_args.length == 6) { + if (!_args[5].getOutput().isScalarBoolean()) raiseValidateError( - "The 6th ctable parameter (outputEmptyBlocks) must be a boolean literal.", - conditional); + "The 6th ctable parameter (outputEmptyBlocks) must be a boolean literal.", + conditional); } break; default: - raiseValidateError("Invalid number of arguments to table(): " + this.toString(), conditional, - LanguageErrorCodes.INVALID_PARAMETERS); + raiseValidateError("Invalid number of arguments to table(): " + + this.toString(), conditional, LanguageErrorCodes.INVALID_PARAMETERS); } // The dimensions for the output matrix will be known only at the // run time @@ -1199,13 +1201,12 @@ else if(getOpCode() == Builtins.RBIND) { case MOMENT: checkMatrixParam(getFirstExpr()); - if(getThirdExpr() != null) { + if (getThirdExpr() != null) { checkNumParameters(3); checkMatrixParam(getSecondExpr()); checkMatchingDimensions(getFirstExpr(), getSecondExpr()); checkScalarParam(getThirdExpr()); - } - else { + } else { checkNumParameters(2); checkScalarParam(getSecondExpr()); } @@ -1221,17 +1222,16 @@ else if(getOpCode() == Builtins.RBIND) { /* * x = cov(V1,V2) or xw = cov(V1,V2,W) */ - if(getThirdExpr() != null) { + if (getThirdExpr() != null) { checkNumParameters(3); - } - else { + } else { checkNumParameters(2); } checkMatrixParam(getFirstExpr()); checkMatrixParam(getSecondExpr()); checkMatchingDimensions(getFirstExpr(), getSecondExpr()); - if(getThirdExpr() != null) { + if (getThirdExpr() != null) { checkMatrixParam(getThirdExpr()); checkMatchingDimensions(getFirstExpr(), getThirdExpr()); } @@ -1245,14 +1245,15 @@ else if(getOpCode() == Builtins.RBIND) { case QUANTILE: /* - * q = quantile(V1,0.5) computes median in V1 or Q = quantile(V1,P) computes the vector of quantiles as - * specified by P or qw = quantile(V1,W,0.5) computes median when weights (W) are given or QW = - * quantile(V1,W,P) computes the vector of quantiles as specified by P, when weights (W) are given + * q = quantile(V1,0.5) computes median in V1 + * or Q = quantile(V1,P) computes the vector of quantiles as specified by P + * or qw = quantile(V1,W,0.5) computes median when weights (W) are given + * or QW = quantile(V1,W,P) computes the vector of quantiles as specified by P, + * when weights (W) are given */ - if(getThirdExpr() != null) { + if (getThirdExpr() != null) { checkNumParameters(3); - } - else { + } else { checkNumParameters(2); } @@ -1260,7 +1261,7 @@ else if(getOpCode() == Builtins.RBIND) { check1DMatrixParam(getFirstExpr()); // check for matching dimensions for other matrix parameters - if(getThirdExpr() != null) { + if (getThirdExpr() != null) { checkMatrixParam(getSecondExpr()); checkMatchingDimensions(getFirstExpr(), getSecondExpr()); } @@ -1269,12 +1270,11 @@ else if(getOpCode() == Builtins.RBIND) { // output dimensions = dimensions of second, if third is null // = dimensions of the third, otherwise. - if(getThirdExpr() != null) { + if (getThirdExpr() != null) { output.setDimensions(getThirdExpr().getOutput().getDim1(), getThirdExpr().getOutput().getDim2()); output.setBlocksize(getThirdExpr().getOutput().getBlocksize()); output.setDataType(getThirdExpr().getOutput().getDataType()); - } - else { + } else { output.setDimensions(getSecondExpr().getOutput().getDim1(), getSecondExpr().getOutput().getDim2()); output.setBlocksize(getSecondExpr().getOutput().getBlocksize()); output.setDataType(getSecondExpr().getOutput().getDataType()); @@ -1282,24 +1282,23 @@ else if(getOpCode() == Builtins.RBIND) { break; case INTERQUANTILE: - if(getThirdExpr() != null) { + if (getThirdExpr() != null) { checkNumParameters(3); - } - else { + } else { checkNumParameters(2); } checkMatrixParam(getFirstExpr()); - if(getThirdExpr() != null) { + if (getThirdExpr() != null) { // i.e., second input is weight vector checkMatrixParam(getSecondExpr()); checkMatchingDimensionsQuantile(); } - if((getThirdExpr() == null && getSecondExpr().getOutput().getDataType() != DataType.SCALAR) && - (getThirdExpr() != null && getThirdExpr().getOutput().getDataType() != DataType.SCALAR)) { + if ((getThirdExpr() == null && getSecondExpr().getOutput().getDataType() != DataType.SCALAR) + && (getThirdExpr() != null && getThirdExpr().getOutput().getDataType() != DataType.SCALAR)) { raiseValidateError("Invalid parameters to " + this.getOpCode(), conditional, - LanguageErrorCodes.INVALID_PARAMETERS); + LanguageErrorCodes.INVALID_PARAMETERS); } output.setValueType(id.getValueType()); @@ -1313,15 +1312,14 @@ else if(getOpCode() == Builtins.RBIND) { /* * Usage: iqm = InterQuartileMean(A,W); iqm = InterQuartileMean(A); */ - if(getSecondExpr() != null) { + if (getSecondExpr() != null) { checkNumParameters(2); - } - else { + } else { checkNumParameters(1); } checkMatrixParam(getFirstExpr()); - if(getSecondExpr() != null) { + if (getSecondExpr() != null) { // i.e., second input is weight vector checkMatrixParam(getSecondExpr()); checkMatchingDimensions(getFirstExpr(), getSecondExpr()); @@ -1351,7 +1349,7 @@ else if(getOpCode() == Builtins.RBIND) { checkNumParameters((getSecondExpr() != null) ? 2 : 1); checkMatrixParam(getFirstExpr()); - if(getSecondExpr() != null) { + if (getSecondExpr() != null) { // i.e., second input is weight vector checkMatrixParam(getSecondExpr()); checkMatchingDimensions(getFirstExpr(), getSecondExpr()); @@ -1368,51 +1366,52 @@ else if(getOpCode() == Builtins.RBIND) { case SAMPLE: { Expression[] in = getAllExpr(); - for(Expression e : in) + for (Expression e : in) checkScalarParam(e); - if(in[0].getOutput().getValueType() != ValueType.FP64 && - in[0].getOutput().getValueType() != ValueType.INT64) + if (in[0].getOutput().getValueType() != ValueType.FP64 + && in[0].getOutput().getValueType() != ValueType.INT64) throw new LanguageException("First argument to sample() must be a number."); - if(in[1].getOutput().getValueType() != ValueType.FP64 && - in[1].getOutput().getValueType() != ValueType.INT64) + if (in[1].getOutput().getValueType() != ValueType.FP64 + && in[1].getOutput().getValueType() != ValueType.INT64) throw new LanguageException("Second argument to sample() must be a number."); boolean check = false; - if(isConstant(in[0]) && isConstant(in[1])) { + if (isConstant(in[0]) && isConstant(in[1])) { long range = ((ConstIdentifier) in[0]).getLongValue(); long size = ((ConstIdentifier) in[1]).getLongValue(); - if(range < size) + if (range < size) check = true; } - if(in.length == 4) { + if (in.length == 4) { checkNumParameters(4); - if(in[3].getOutput().getValueType() != ValueType.INT64) + if (in[3].getOutput().getValueType() != ValueType.INT64) throw new LanguageException("Fourth argument, seed, to sample() must be an integer value."); - if(in[2].getOutput().getValueType() != ValueType.BOOLEAN) + if (in[2].getOutput().getValueType() != ValueType.BOOLEAN) throw new LanguageException( - "Third argument to sample() must either denote replacement policy (boolean) or seed (integer)."); - } - else if(in.length == 3) { + "Third argument to sample() must either denote replacement policy (boolean) or seed (integer)."); + } else if (in.length == 3) { checkNumParameters(3); - if(in[2].getOutput().getValueType() != ValueType.BOOLEAN && - in[2].getOutput().getValueType() != ValueType.INT64) + if (in[2].getOutput().getValueType() != ValueType.BOOLEAN + && in[2].getOutput().getValueType() != ValueType.INT64) throw new LanguageException( - "Third argument to sample() must either denote replacement policy (boolean) or seed (integer)."); + "Third argument to sample() must either denote replacement policy (boolean) or seed (integer)."); } - if(check && in.length >= 3 && isConstant(in[2]) && - in[2].getOutput().getValueType() == ValueType.BOOLEAN && !((BooleanIdentifier) in[2]).getValue()) - throw new LanguageException( - "Sample (size=" + ((ConstIdentifier) in[0]).getLongValue() + ") larger than population (size=" - + ((ConstIdentifier) in[1]).getLongValue() + ") can only be generated with replacement."); + if (check && in.length >= 3 + && isConstant(in[2]) + && in[2].getOutput().getValueType() == ValueType.BOOLEAN + && !((BooleanIdentifier) in[2]).getValue()) + throw new LanguageException("Sample (size=" + ((ConstIdentifier) in[0]).getLongValue() + + ") larger than population (size=" + ((ConstIdentifier) in[1]).getLongValue() + + ") can only be generated with replacement."); // Output is a column vector output.setDataType(DataType.MATRIX); output.setValueType(ValueType.FP64); - if(isConstant(in[1])) + if (isConstant(in[1])) output.setDimensions(((ConstIdentifier) in[1]).getLongValue(), 1); else output.setDimensions(-1, 1); @@ -1425,30 +1424,29 @@ else if(in.length == 3) { // basic parameter validation checkScalarParam(getFirstExpr()); checkScalarParam(getSecondExpr()); - if(getThirdExpr() != null) { + if (getThirdExpr() != null) { checkNumParameters(3); checkScalarParam(getThirdExpr()); - } - else + } else checkNumParameters(2); // constant propagation (from, to, incr) - if(!conditional) { - if(getFirstExpr() instanceof DataIdentifier && - constVars.containsKey(((DataIdentifier) getFirstExpr()).getName())) + if (!conditional) { + if (getFirstExpr() instanceof DataIdentifier + && constVars.containsKey(((DataIdentifier) getFirstExpr()).getName())) _args[0] = constVars.get(((DataIdentifier) getFirstExpr()).getName()); - if(getSecondExpr() instanceof DataIdentifier && - constVars.containsKey(((DataIdentifier) getSecondExpr()).getName())) + if (getSecondExpr() instanceof DataIdentifier + && constVars.containsKey(((DataIdentifier) getSecondExpr()).getName())) _args[1] = constVars.get(((DataIdentifier) getSecondExpr()).getName()); - if(getThirdExpr() != null && getThirdExpr() instanceof DataIdentifier && - constVars.containsKey(((DataIdentifier) getThirdExpr()).getName())) + if (getThirdExpr() != null && getThirdExpr() instanceof DataIdentifier + && constVars.containsKey(((DataIdentifier) getThirdExpr()).getName())) _args[2] = constVars.get(((DataIdentifier) getThirdExpr()).getName()); } // check if dimensions can be inferred long dim1 = -1, dim2 = 1; - if(isConstant(getFirstExpr()) && isConstant(getSecondExpr()) && - (getThirdExpr() != null ? isConstant(getThirdExpr()) : true)) { + if (isConstant(getFirstExpr()) && isConstant(getSecondExpr()) + && (getThirdExpr() != null ? isConstant(getThirdExpr()) : true)) { double from, to, incr; try { from = getDoubleValue(getFirstExpr()); @@ -1456,18 +1454,17 @@ else if(in.length == 3) { // Setup the value of increment // default value: 1 if from <= to; -1 if from > to - if(getThirdExpr() == null) { + if (getThirdExpr() == null) { expandArguments(); _args[2] = new DoubleIdentifier(((from > to) ? -1.0 : 1.0), this); } incr = getDoubleValue(getThirdExpr()); - } - catch(LanguageException e) { + } catch (LanguageException e) { throw new LanguageException("Arguments for seq() must be numeric."); } - if((from > to) && (incr >= 0)) + if ((from > to) && (incr >= 0)) throw new LanguageException("Wrong sign for the increment in a call to seq()"); // Both end points of the range must included i.e., [from,to] both inclusive. @@ -1487,12 +1484,12 @@ else if(in.length == 3) { checkMatrixParam(getFirstExpr()); checkMatrixParam(getSecondExpr()); - if(getSecondExpr().getOutput().dimsKnown() && !is1DMatrix(getSecondExpr())) + if (getSecondExpr().getOutput().dimsKnown() && !is1DMatrix(getSecondExpr())) raiseValidateError("Second input to solve() must be a vector", conditional); - if(getFirstExpr().getOutput().dimsKnown() && getSecondExpr().getOutput().dimsKnown() && - getFirstExpr().getOutput().getDim1() != getSecondExpr().getOutput().getDim1() && - getFirstExpr().getOutput().getDim1() != getFirstExpr().getOutput().getDim2()) + if (getFirstExpr().getOutput().dimsKnown() && getSecondExpr().getOutput().dimsKnown() && + getFirstExpr().getOutput().getDim1() != getSecondExpr().getOutput().getDim1() && + getFirstExpr().getOutput().getDim1() != getFirstExpr().getOutput().getDim2()) raiseValidateError("Dimension mismatch in a call to solve()", conditional); output.setDataType(DataType.MATRIX); @@ -1509,9 +1506,9 @@ else if(in.length == 3) { output.setValueType(ValueType.FP64); Identifier in = getFirstExpr().getOutput(); - if(in.dimsKnown() && in.getDim1() != in.getDim2()) + if (in.dimsKnown() && in.getDim1() != in.getDim2()) raiseValidateError("Input to inv() must be square matrix -- given: a " + in.getDim1() + "x" - + in.getDim2() + " matrix.", conditional); + + in.getDim2() + " matrix.", conditional); output.setDimensions(in.getDim1(), in.getDim2()); output.setBlocksize(in.getBlocksize()); @@ -1526,9 +1523,9 @@ else if(in.length == 3) { output.setValueType(ValueType.FP64); Identifier inA = getFirstExpr().getOutput(); - if(inA.dimsKnown() && inA.getDim1() != inA.getDim2()) + if (inA.dimsKnown() && inA.getDim1() != inA.getDim2()) raiseValidateError("Input to cholesky() must be square matrix -- given: a " + inA.getDim1() + "x" - + inA.getDim2() + " matrix.", conditional); + + inA.getDim2() + " matrix.", conditional); output.setDimensions(inA.getDim1(), inA.getDim2()); output.setBlocksize(inA.getBlocksize()); @@ -1544,9 +1541,10 @@ else if(in.length == 3) { checkMatrixParam(getSecondExpr()); checkScalarParam(getThirdExpr()); checkValueTypeParam(getThirdExpr(), ValueType.STRING); - if(id.getDim2() > 1 || id2.getDim1() > 1) { - raiseValidateError("Outer vector operations require a common dimension of one: " + id.getDim1() - + "x" + id.getDim2() + " o " + id2.getDim1() + "x" + id2.getDim2() + ".", false); + if (id.getDim2() > 1 || id2.getDim1() > 1) { + raiseValidateError("Outer vector operations require a common dimension of one: " + + id.getDim1() + "x" + id.getDim2() + " o " + id2.getDim1() + "x" + id2.getDim2() + ".", + false); } // set output characteristics @@ -1590,7 +1588,7 @@ else if(in.length == 3) { // this is filter Expression input2 = null; - if(!(this.getOpCode() == Builtins.MAX_POOL || this.getOpCode() == Builtins.AVG_POOL)) { + if (!(this.getOpCode() == Builtins.MAX_POOL || this.getOpCode() == Builtins.AVG_POOL)) { input2 = _args[1]; // For conv2d_backward functions, this is dout checkMatrixParam(input2); } @@ -1598,15 +1596,14 @@ else if(in.length == 3) { output.setValueType(ValueType.FP64); output.setBlocksize(input.getOutput().getBlocksize()); - if(this.getOpCode() == Builtins.MAX_POOL_BACKWARD || this.getOpCode() == Builtins.AVG_POOL_BACKWARD) { + if (this.getOpCode() == Builtins.MAX_POOL_BACKWARD || this.getOpCode() == Builtins.AVG_POOL_BACKWARD) { output.setDimensions(input.getOutput().getDim1(), input.getOutput().getDim2()); - } - else { + } else { // stride1, stride2, padding1, padding2, numImg, numChannels, imgSize, imgSize, // filter_shape1=1, filter_shape2=1, filterSize/poolSize1, filterSize/poolSize1 try { int start = 2; - if(!(this.getOpCode() == Builtins.MAX_POOL || this.getOpCode() == Builtins.AVG_POOL)) { + if (!(this.getOpCode() == Builtins.MAX_POOL || this.getOpCode() == Builtins.AVG_POOL)) { start = 1; } long stride_h = (long) getDoubleValue(_args[start++]); @@ -1618,7 +1615,7 @@ else if(in.length == 3) { long H = (long) getDoubleValue(_args[start++]); long W = (long) getDoubleValue(_args[start++]); long K = -1; - if(!(this.getOpCode() == Builtins.MAX_POOL || this.getOpCode() == Builtins.AVG_POOL)) { + if (!(this.getOpCode() == Builtins.MAX_POOL || this.getOpCode() == Builtins.AVG_POOL)) { K = (long) getDoubleValue(_args[start]); } start++; @@ -1626,42 +1623,38 @@ else if(in.length == 3) { long R = (long) getDoubleValue(_args[start++]); long S = (long) getDoubleValue(_args[start++]); - if(this.getOpCode() == Builtins.CONV2D_BACKWARD_FILTER) { + if (this.getOpCode() == Builtins.CONV2D_BACKWARD_FILTER) { output.setDimensions(K, C * R * S); - } - else if(this.getOpCode() == Builtins.CONV2D_BACKWARD_DATA) { + } else if (this.getOpCode() == Builtins.CONV2D_BACKWARD_DATA) { output.setDimensions(N, C * H * W); - } - else if(H > 0 && W > 0 && stride_h > 0 && stride_w > 0 && pad_h >= 0 && pad_w >= 0 && R > 0 && - S > 0) { + } else if (H > 0 && W > 0 && stride_h > 0 && stride_w > 0 && pad_h >= 0 && pad_w >= 0 && R > 0 + && S > 0) { long P = DnnUtils.getP(H, R, stride_h, pad_h); long Q = DnnUtils.getQ(W, S, stride_w, pad_w); // Try to set both rows and columns - if(this.getOpCode() == Builtins.CONV2D) + if (this.getOpCode() == Builtins.CONV2D) output.setDimensions(N, K * P * Q); - else if(this.getOpCode() == Builtins.MAX_POOL || this.getOpCode() == Builtins.AVG_POOL) + else if (this.getOpCode() == Builtins.MAX_POOL || this.getOpCode() == Builtins.AVG_POOL) output.setDimensions(N, C * P * Q); else throw new LanguageException(""); - } - else { + } else { // Since columns cannot be computed, set only rows - if(this.getOpCode() == Builtins.CONV2D) + if (this.getOpCode() == Builtins.CONV2D) output.setDimensions(input.getOutput().getDim1(), -1); - else if(this.getOpCode() == Builtins.MAX_POOL || this.getOpCode() == Builtins.AVG_POOL) + else if (this.getOpCode() == Builtins.MAX_POOL || this.getOpCode() == Builtins.AVG_POOL) output.setDimensions(input.getOutput().getDim1(), -1); else throw new LanguageException(""); } - } - catch(Exception e) { + } catch (Exception e) { output.setDimensions(-1, -1); // To make sure that output dimensions are not incorrect even if // getDoubleValue doesnot return value } } checkMatrixParam(input); - if(input2 != null) + if (input2 != null) checkMatrixParam(input2); break; } @@ -1707,40 +1700,37 @@ else if(this.getOpCode() == Builtins.MAX_POOL || this.getOpCode() == Builtins.AV checkNumParameters(getThirdExpr() != null ? 3 : 2); checkMatrixFrameParam(getFirstExpr()); checkScalarParam(getSecondExpr()); - if(getThirdExpr() != null) + if (getThirdExpr() != null) checkScalarParam(getThirdExpr()); // margin output.setDataType(DataType.FRAME); - if(_args[1].getText().contains("jaccardSim")) { + if (_args[1].getText().contains("jaccardSim")) { output.setDimensions(id.getDim1(), id.getDim1()); output.setValueType(ValueType.FP64); - } - else { + } else { output.setDimensions(id.getDim1(), id.getDim2()); output.setValueType(ValueType.STRING); } break; case LOCAL: - if(OptimizerUtils.ALLOW_SCRIPT_LEVEL_LOCAL_COMMAND) { + if (OptimizerUtils.ALLOW_SCRIPT_LEVEL_LOCAL_COMMAND) { checkNumParameters(1); checkMatrixParam(getFirstExpr()); output.setDataType(DataType.MATRIX); output.setDimensions(id.getDim1(), id.getDim2()); output.setBlocksize(id.getBlocksize()); output.setValueType(id.getValueType()); - } - else + } else raiseValidateError("Local instruction not allowed in dml script"); case COMPRESS: case DECOMPRESS: - if(OptimizerUtils.ALLOW_SCRIPT_LEVEL_COMPRESS_COMMAND) { + if (OptimizerUtils.ALLOW_SCRIPT_LEVEL_COMPRESS_COMMAND) { checkNumParameters(1); checkMatrixParam(getFirstExpr()); output.setDataType(DataType.MATRIX); output.setDimensions(id.getDim1(), id.getDim2()); output.setBlocksize(id.getBlocksize()); output.setValueType(id.getValueType()); - } - else + } else raiseValidateError("Compress/DeCompress instruction not allowed in dml script"); break; case ROW_COUNT_DISTINCT: @@ -1764,14 +1754,13 @@ else if(this.getOpCode() == Builtins.MAX_POOL || this.getOpCode() == Builtins.AV break; default: - if(isMathFunction()) { + if (isMathFunction()) { checkMathFunctionParam(); // unary operations - if(getSecondExpr() == null) { + if (getSecondExpr() == null) { output.setDataType(id.getDataType()); - output - .setValueType((output.getDataType() == DataType.SCALAR && getOpCode() == Builtins.ABS) ? id - .getValueType() : ValueType.FP64); + output.setValueType((output.getDataType() == DataType.SCALAR + && getOpCode() == Builtins.ABS) ? id.getValueType() : ValueType.FP64); output.setDimensions(id.getDim1(), id.getDim2()); output.setBlocksize(id.getBlocksize()); } @@ -1779,18 +1768,17 @@ else if(this.getOpCode() == Builtins.MAX_POOL || this.getOpCode() == Builtins.AV else { setBinaryOutputProperties(output); // override computed value type for special cases - if(getOpCode() == Builtins.LOG) + if (getOpCode() == Builtins.LOG) output.setValueType(ValueType.FP64); } - } - else { + } else { // always unconditional (because unsupported operation) Builtins op = getOpCode(); - if(op == Builtins.EIGEN || op == Builtins.LU || op == Builtins.QR || op == Builtins.SVD || - op == Builtins.LSTM || op == Builtins.LSTM_BACKWARD || op == Builtins.BATCH_NORM2D || - op == Builtins.BATCH_NORM2D_BACKWARD) + if (op == Builtins.EIGEN || op == Builtins.LU || op == Builtins.QR || op == Builtins.SVD + || op == Builtins.LSTM || op == Builtins.LSTM_BACKWARD + || op == Builtins.BATCH_NORM2D || op == Builtins.BATCH_NORM2D_BACKWARD) raiseValidateError("Function " + op + " needs to be called with multi-return assignment.", - false, LanguageErrorCodes.INVALID_PARAMETERS); + false, LanguageErrorCodes.INVALID_PARAMETERS); else raiseValidateError("Unsupported function " + op, false, LanguageErrorCodes.INVALID_PARAMETERS); } @@ -1801,12 +1789,12 @@ private void setBinaryOutputProperties(DataIdentifier output) { DataType dt1 = getFirstExpr().getOutput().getDataType(); DataType dt2 = getSecondExpr().getOutput().getDataType(); DataType dtOut = (dt1 == DataType.MATRIX || dt2 == DataType.MATRIX) ? DataType.MATRIX : DataType.SCALAR; - if(dt1 == DataType.MATRIX && dt2 == DataType.MATRIX) + if (dt1 == DataType.MATRIX && dt2 == DataType.MATRIX) checkMatchingDimensions(getFirstExpr(), getSecondExpr(), true); MatrixCharacteristics dims = getBinaryMatrixCharacteristics(getFirstExpr(), getSecondExpr()); output.setDataType(dtOut); output.setValueType( - dtOut == DataType.MATRIX ? ValueType.FP64 : computeValueType(getFirstExpr(), getSecondExpr(), true)); + dtOut == DataType.MATRIX ? ValueType.FP64 : computeValueType(getFirstExpr(), getSecondExpr(), true)); output.setDimensions(dims.getRows(), dims.getCols()); output.setBlocksize(dims.getBlocksize()); } @@ -1816,42 +1804,42 @@ private void setTernaryOutputProperties(DataIdentifier output, boolean condition DataType dt2 = getSecondExpr().getOutput().getDataType(); DataType dt3 = getThirdExpr().getOutput().getDataType(); DataType dtOut = (dt1.isMatrix() || dt2.isMatrix() || dt3.isMatrix()) ? DataType.MATRIX : DataType.SCALAR; - if(dt1 == DataType.MATRIX && dt2 == DataType.MATRIX) + if (dt1 == DataType.MATRIX && dt2 == DataType.MATRIX) checkMatchingDimensions(getFirstExpr(), getSecondExpr(), false, conditional); - if(dt1 == DataType.MATRIX && dt3 == DataType.MATRIX) + if (dt1 == DataType.MATRIX && dt3 == DataType.MATRIX) checkMatchingDimensions(getFirstExpr(), getThirdExpr(), false, conditional); - if(dt2 == DataType.MATRIX && dt3 == DataType.MATRIX) + if (dt2 == DataType.MATRIX && dt3 == DataType.MATRIX) checkMatchingDimensions(getSecondExpr(), getThirdExpr(), false, conditional); MatrixCharacteristics dims1 = getBinaryMatrixCharacteristics(getFirstExpr(), getSecondExpr()); MatrixCharacteristics dims2 = getBinaryMatrixCharacteristics(getSecondExpr(), getThirdExpr()); output.setDataType(dtOut); output.setValueType( - dtOut == DataType.MATRIX ? ValueType.FP64 : computeValueType(getSecondExpr(), getThirdExpr(), true)); + dtOut == DataType.MATRIX ? ValueType.FP64 : computeValueType(getSecondExpr(), getThirdExpr(), true)); output.setDimensions(Math.max(dims1.getRows(), dims2.getRows()), Math.max(dims1.getCols(), dims2.getCols())); output.setBlocksize(Math.max(dims1.getBlocksize(), dims2.getBlocksize())); } private void setNaryOutputProperties(DataIdentifier output) { - DataType dt = Arrays.stream(getAllExpr()) - .allMatch(e -> e.getOutput().getDataType().isScalar()) ? DataType.SCALAR : DataType.MATRIX; - Expression firstM = dt.isMatrix() ? Arrays.stream(getAllExpr()) - .filter(e -> e.getOutput().getDataType().isMatrix()).findFirst().get() : null; + DataType dt = Arrays.stream(getAllExpr()).allMatch( + e -> e.getOutput().getDataType().isScalar()) ? DataType.SCALAR : DataType.MATRIX; + Expression firstM = dt.isMatrix() ? Arrays.stream(getAllExpr()).filter( + e -> e.getOutput().getDataType().isMatrix()).findFirst().get() : null; ValueType vt = dt.isMatrix() ? ValueType.FP64 : ValueType.INT64; - for(Expression e : getAllExpr()) { + for (Expression e : getAllExpr()) { vt = computeValueType(e, e.getOutput().getValueType(), vt, true); - if(e.getOutput().getDataType().isMatrix()) + if (e.getOutput().getDataType().isMatrix()) checkMatchingDimensions(firstM, e, true); } output.setDataType(dt); output.setValueType(vt); output.setDimensions(dt.isMatrix() ? firstM.getOutput().getDim1() : 0, - dt.isMatrix() ? firstM.getOutput().getDim2() : 0); + dt.isMatrix() ? firstM.getOutput().getDim2() : 0); output.setBlocksize(dt.isMatrix() ? firstM.getOutput().getBlocksize() : 0); } private void expandArguments() { - if(_args == null) { + if (_args == null) { _args = new Expression[1]; return; } @@ -1866,20 +1854,20 @@ public boolean multipleReturns() { } private static boolean isConstant(Expression expr) { - return(expr != null && expr instanceof ConstIdentifier); + return (expr != null && expr instanceof ConstIdentifier); } private static double getDoubleValue(Expression expr) { - if(expr instanceof DoubleIdentifier) + if (expr instanceof DoubleIdentifier) return ((DoubleIdentifier) expr).getValue(); - else if(expr instanceof IntIdentifier) + else if (expr instanceof IntIdentifier) return ((IntIdentifier) expr).getValue(); else throw new LanguageException("Expecting a numeric value."); } private boolean isMathFunction() { - switch(this.getOpCode()) { + switch (this.getOpCode()) { case COS: case SIN: case TAN: @@ -1911,7 +1899,7 @@ private boolean isMathFunction() { } private void checkMathFunctionParam() { - switch(this.getOpCode()) { + switch (this.getOpCode()) { case COS: case SIN: case TAN: @@ -1932,10 +1920,9 @@ private void checkMathFunctionParam() { checkNumParameters(1); break; case LOG: - if(getSecondExpr() != null) { + if (getSecondExpr() != null) { checkNumParameters(2); - } - else { + } else { checkNumParameters(1); } break; @@ -1948,9 +1935,9 @@ private void checkMathFunctionParam() { @Override public String toString() { StringBuilder sb = new StringBuilder(_opcode.toString() + "("); - if(_args != null) { - for(int i = 0; i < _args.length; i++) { - if(i > 0) { + if (_args != null) { + for (int i = 0; i < _args.length; i++) { + if (i > 0) { sb.append(","); } sb.append(_args[i].toString()); @@ -1965,7 +1952,7 @@ public String toString() { public VariableSet variablesRead() { VariableSet result = new VariableSet(); - for(int i = 0; i < _args.length; i++) { + for (int i = 0; i < _args.length; i++) { result.addVariables(_args[i].variablesRead()); } @@ -1980,116 +1967,113 @@ public VariableSet variablesUpdated() { } protected void checkNumParameters(int count) { // always unconditional - if(getFirstExpr() == null && _args.length > 0) { + if (getFirstExpr() == null && _args.length > 0) { raiseValidateError("Missing argument for function " + this.getOpCode(), false, - LanguageErrorCodes.INVALID_PARAMETERS); + LanguageErrorCodes.INVALID_PARAMETERS); } // Not sure the rationale for the first two if loops, but will keep them for // backward compatibility - if(((count == 1) && (getSecondExpr() != null || getThirdExpr() != null)) || - ((count == 2) && (getThirdExpr() != null))) { + if (((count == 1) && (getSecondExpr() != null || getThirdExpr() != null)) + || ((count == 2) && (getThirdExpr() != null))) { raiseValidateError("Invalid number of arguments for function " + this.getOpCode().toString().toLowerCase() - + "(). This function only takes 1 or 2 arguments.", false); - } - else if(((count == 2) && (getSecondExpr() == null)) || - ((count == 3) && (getSecondExpr() == null || getThirdExpr() == null))) { + + "(). This function only takes 1 or 2 arguments.", false); + } else if (((count == 2) && (getSecondExpr() == null)) + || ((count == 3) && (getSecondExpr() == null || getThirdExpr() == null))) { raiseValidateError("Missing argument for function " + this.getOpCode(), false, - LanguageErrorCodes.INVALID_PARAMETERS); - } - else if(count > 0 && (_args == null || _args.length < count)) { + LanguageErrorCodes.INVALID_PARAMETERS); + } else if (count > 0 && (_args == null || _args.length < count)) { raiseValidateError("Missing argument for function " + this.getOpCode(), false, - LanguageErrorCodes.INVALID_PARAMETERS); - } - else if(count == 0 && (_args.length > 0 || getSecondExpr() != null || getThirdExpr() != null)) { - raiseValidateError( - "Missing argument for function " + this.getOpCode() + "(). This function doesn't take any arguments.", - false); + LanguageErrorCodes.INVALID_PARAMETERS); + } else if (count == 0 && (_args.length > 0 + || getSecondExpr() != null || getThirdExpr() != null)) { + raiseValidateError("Missing argument for function " + this.getOpCode() + + "(). This function doesn't take any arguments.", false); } } protected void checkMatrixParam(Expression e) { - if(e.getOutput().getDataType() != DataType.MATRIX) { - raiseValidateError("Expected " + e.getText() + " to be a matrix argument for function " - + this.getOpCode().toString().toLowerCase() + "().", false); + if (e.getOutput().getDataType() != DataType.MATRIX) { + raiseValidateError("Expected " + e.getText() + " to be a matrix argument for function " + + this.getOpCode().toString().toLowerCase() + "().", false); } } protected void checkMatrixTensorParam(Expression e) { - if(e.getOutput().getDataType() != DataType.MATRIX) { + if (e.getOutput().getDataType() != DataType.MATRIX) { // Param is not a matrix // TODO get supported Operations form builtins - if(e.getOutput().getDataType() != DataType.TENSOR || getOpCode() != Builtins.SUM) { + if (e.getOutput().getDataType() != DataType.TENSOR || getOpCode() != Builtins.SUM) { // Param is also not a tensor, or the operation is not supported on tensor raiseValidateError("Expected " + e.getText() + " to be a matrix or tensor argument for function " - + this.getOpCode().toString().toLowerCase() + "().", false); + + this.getOpCode().toString().toLowerCase() + "().", false); } } } protected void checkDataTypeParam(Expression e, DataType... dt) { // always unconditional - if(!ArrayUtils.contains(dt, e.getOutput().getDataType())) + if (!ArrayUtils.contains(dt, e.getOutput().getDataType())) raiseValidateError("Non-matching expected data type for function " + getOpCode(), false, - LanguageErrorCodes.UNSUPPORTED_PARAMETERS); + LanguageErrorCodes.UNSUPPORTED_PARAMETERS); } protected void checkMatrixFrameParam(Expression e) { // always unconditional - if(e.getOutput().getDataType() != DataType.MATRIX && e.getOutput().getDataType() != DataType.FRAME) { + if (e.getOutput().getDataType() != DataType.MATRIX && e.getOutput().getDataType() != DataType.FRAME) { raiseValidateError("Expecting matrix or frame parameter for function " + getOpCode(), false, - LanguageErrorCodes.UNSUPPORTED_PARAMETERS); + LanguageErrorCodes.UNSUPPORTED_PARAMETERS); } } protected void checkMatrixScalarParam(Expression e) { // always unconditional - if(e.getOutput().getDataType() != DataType.MATRIX && e.getOutput().getDataType() != DataType.SCALAR) { + if (e.getOutput().getDataType() != DataType.MATRIX && e.getOutput().getDataType() != DataType.SCALAR) { raiseValidateError("Expecting matrix or scalar parameter for function " + getOpCode(), false, - LanguageErrorCodes.UNSUPPORTED_PARAMETERS); + LanguageErrorCodes.UNSUPPORTED_PARAMETERS); } } private void checkScalarParam(Expression e) { // always unconditional - if(e.getOutput().getDataType() != DataType.SCALAR) { + if (e.getOutput().getDataType() != DataType.SCALAR) { raiseValidateError("Expecting scalar parameter for function " + getOpCode(), false, - LanguageErrorCodes.UNSUPPORTED_PARAMETERS); + LanguageErrorCodes.UNSUPPORTED_PARAMETERS); } } private void checkListParam(Expression e) { // always unconditional - if(e.getOutput().getDataType() != DataType.LIST) { + if (e.getOutput().getDataType() != DataType.LIST) { raiseValidateError("Expecting scalar parameter for function " + getOpCode(), false, - LanguageErrorCodes.UNSUPPORTED_PARAMETERS); + LanguageErrorCodes.UNSUPPORTED_PARAMETERS); } } @SuppressWarnings("unused") private void checkScalarFrameParam(Expression e) { // always unconditional - if(e.getOutput().getDataType() != DataType.SCALAR && e.getOutput().getDataType() != DataType.FRAME) { + if (e.getOutput().getDataType() != DataType.SCALAR && e.getOutput().getDataType() != DataType.FRAME) { raiseValidateError("Expecting scalar parameter for function " + getOpCode(), false, - LanguageErrorCodes.UNSUPPORTED_PARAMETERS); + LanguageErrorCodes.UNSUPPORTED_PARAMETERS); } } private void checkValueTypeParam(Expression e, ValueType vt) { // always unconditional - if(e.getOutput().getValueType() != vt) { + if (e.getOutput().getValueType() != vt) { raiseValidateError("Expecting parameter of different value type " + this.getOpCode(), false, - LanguageErrorCodes.UNSUPPORTED_PARAMETERS); + LanguageErrorCodes.UNSUPPORTED_PARAMETERS); } } protected void checkStringOrDataIdentifier(Expression e) { // always unconditional - if(!(e.getOutput().getDataType().isScalar() && e.getOutput().getValueType() == ValueType.STRING) && - !(e instanceof DataIdentifier && !(e instanceof IndexedIdentifier))) { + if (!(e.getOutput().getDataType().isScalar() && e.getOutput().getValueType() == ValueType.STRING) + && !(e instanceof DataIdentifier && !(e instanceof IndexedIdentifier))) { raiseValidateError("Expecting variable name or data identifier " + getOpCode(), false, - LanguageErrorCodes.UNSUPPORTED_PARAMETERS); + LanguageErrorCodes.UNSUPPORTED_PARAMETERS); } } private static boolean is1DMatrix(Expression e) { - return(e.getOutput().getDim1() == 1 || e.getOutput().getDim2() == 1); + return (e.getOutput().getDim1() == 1 || e.getOutput().getDim2() == 1); } private static boolean dimsKnown(Expression e) { - return(e.getOutput().getDim1() != -1 && e.getOutput().getDim2() != -1); + return (e.getOutput().getDim1() != -1 && e.getOutput().getDim2() != -1); } private void check1DMatrixParam(Expression e) { // always unconditional @@ -2098,9 +2082,9 @@ private void check1DMatrixParam(Expression e) { // always unconditional // throw an exception, when e's output is NOT a one-dimensional matrix // the check must be performed only when the dimensions are known at compilation // time - if(dimsKnown(e) && !is1DMatrix(e)) { - raiseValidateError("Expecting one-dimensional matrix parameter for function " + this.getOpCode(), false, - LanguageErrorCodes.UNSUPPORTED_PARAMETERS); + if (dimsKnown(e) && !is1DMatrix(e)) { + raiseValidateError("Expecting one-dimensional matrix parameter for function " + + this.getOpCode(), false, LanguageErrorCodes.UNSUPPORTED_PARAMETERS); } } @@ -2113,55 +2097,59 @@ private void checkMatchingDimensions(Expression expr1, Expression expr2, boolean } private void checkMatchingDimensions(Expression expr1, Expression expr2, boolean allowsMV, boolean conditional) { - if(expr1 != null && expr2 != null) { + if (expr1 != null && expr2 != null) { // if any matrix has unknown dimensions, simply return - if(expr1.getOutput().getDim1() == -1 || expr2.getOutput().getDim1() == -1 || - expr1.getOutput().getDim2() == -1 || expr2.getOutput().getDim2() == -1) { + if (expr1.getOutput().getDim1() == -1 || expr2.getOutput().getDim1() == -1 + || expr1.getOutput().getDim2() == -1 || expr2.getOutput().getDim2() == -1) { return; - } - else if((!allowsMV && expr1.getOutput().getDim1() != expr2.getOutput().getDim1()) || - (allowsMV && expr1.getOutput().getDim1() != expr2.getOutput().getDim1() && - expr2.getOutput().getDim1() != 1) || - (!allowsMV && expr1.getOutput().getDim2() != expr2.getOutput().getDim2()) || (allowsMV && - expr1.getOutput().getDim2() != expr2.getOutput().getDim2() && expr2.getOutput().getDim2() != 1)) { - raiseValidateError("Mismatch in matrix dimensions of parameters for function " + this.getOpCode(), - conditional, LanguageErrorCodes.INVALID_PARAMETERS); + } else if ((!allowsMV && expr1.getOutput().getDim1() != expr2.getOutput().getDim1()) + || (allowsMV && expr1.getOutput().getDim1() != expr2.getOutput().getDim1() + && expr2.getOutput().getDim1() != 1) + || (!allowsMV && expr1.getOutput().getDim2() != expr2.getOutput().getDim2()) + || (allowsMV && expr1.getOutput().getDim2() != expr2.getOutput().getDim2() + && expr2.getOutput().getDim2() != 1)) { + raiseValidateError("Mismatch in matrix dimensions of parameters for function " + + this.getOpCode(), conditional, LanguageErrorCodes.INVALID_PARAMETERS); } } } private void checkMatchingDimensionsQuantile() { - if(getFirstExpr().getOutput().getDim1() != getSecondExpr().getOutput().getDim1()) { - raiseValidateError("Mismatch in matrix dimensions for " + this.getOpCode(), false, - LanguageErrorCodes.INVALID_PARAMETERS); + if (getFirstExpr().getOutput().getDim1() != getSecondExpr().getOutput().getDim1()) { + raiseValidateError("Mismatch in matrix dimensions for " + + this.getOpCode(), false, LanguageErrorCodes.INVALID_PARAMETERS); } } - public static BuiltinFunctionExpression getBuiltinFunctionExpression(ParserRuleContext ctx, String functionName, - ArrayList paramExprsPassed, String filename) { + public static BuiltinFunctionExpression getBuiltinFunctionExpression(ParserRuleContext ctx, + String functionName, ArrayList paramExprsPassed, String filename) { - if(functionName == null || paramExprsPassed == null) + if (functionName == null || paramExprsPassed == null) return null; // check if the function name is built-in function // (assign built-in function op if function is built-in - return (Builtins.contains(functionName, false, false) && - (paramExprsPassed.stream().anyMatch(p -> p.getName() == null) // at least one unnamed - || paramExprsPassed.size() == 0)) ? new BuiltinFunctionExpression(ctx, Builtins.get(functionName), - paramExprsPassed, filename) : null; + return (Builtins.contains(functionName, false, false) + && (paramExprsPassed.stream().anyMatch(p -> p.getName() == null) // at least one unnamed + || paramExprsPassed.size() == 0)) + ? new BuiltinFunctionExpression(ctx, Builtins.get(functionName), paramExprsPassed, + filename) + : null; } /** - * Convert a value type (double, int, or boolean) to a built-in function operator. + * Convert a value type (double, int, or boolean) to a built-in function + * operator. * - * @param vt Value type ({@code ValueType.DOUBLE}, {@code ValueType.INT}, or {@code ValueType.BOOLEAN}). - * @return Built-in function operator ({@code Builtins.AS_DOUBLE}, {@code Builtins.AS_INT}, or - * {@code Builtins.AS_BOOLEAN}). + * @param vt Value type ({@code ValueType.DOUBLE}, {@code ValueType.INT}, or + * {@code ValueType.BOOLEAN}). + * @return Built-in function operator ({@code Builtins.AS_DOUBLE}, + * {@code Builtins.AS_INT}, or {@code Builtins.AS_BOOLEAN}). */ public static Builtins getValueTypeCastOperator(ValueType vt) { - switch(vt) { + switch (vt) { case FP64: return Builtins.CAST_AS_DOUBLE; case INT64: diff --git a/src/main/java/org/apache/sysds/parser/DMLTranslator.java b/src/main/java/org/apache/sysds/parser/DMLTranslator.java index ac0699f0b2f..537de1abbaa 100644 --- a/src/main/java/org/apache/sysds/parser/DMLTranslator.java +++ b/src/main/java/org/apache/sysds/parser/DMLTranslator.java @@ -92,33 +92,35 @@ import org.apache.sysds.runtime.instructions.Instruction; import org.apache.sysds.runtime.instructions.cp.VariableCPInstruction; -public class DMLTranslator { +public class DMLTranslator +{ private static final Log LOG = LogFactory.getLog(DMLTranslator.class.getName()); private DMLProgram _dmlProg; public DMLTranslator(DMLProgram dmlp) { _dmlProg = dmlp; - // setup default size for unknown dimensions + //setup default size for unknown dimensions OptimizerUtils.resetDefaultSize(); - // reinit rewriter according to opt level flags - Recompiler.reinitRecompiler(); + //reinit rewriter according to opt level flags + Recompiler.reinitRecompiler(); } public void validateParseTree(DMLProgram dmlp) { validateParseTree(dmlp, true); } - public void validateParseTree(DMLProgram dmlp, boolean inclFuns) { - // STEP1: Pre-processing steps for validate - e.g., prepare read-after-write meta data + public void validateParseTree(DMLProgram dmlp, boolean inclFuns) + { + //STEP1: Pre-processing steps for validate - e.g., prepare read-after-write meta data boolean fWriteRead = prepareReadAfterWrite(dmlp, new HashMap()); - // STEP2: Actual Validate - if(inclFuns) { + //STEP2: Actual Validate + if( inclFuns ) { // handle functions in namespaces (current program has default namespace) - for(String namespaceKey : dmlp.getNamespaces().keySet()) { + for (String namespaceKey : dmlp.getNamespaces().keySet()) { // for each function defined in the namespace - for(String fname : dmlp.getFunctionStatementBlocks(namespaceKey).keySet()) { - FunctionStatementBlock fblock = dmlp.getFunctionStatementBlock(namespaceKey, fname); + for (String fname : dmlp.getFunctionStatementBlocks(namespaceKey).keySet()) { + FunctionStatementBlock fblock = dmlp.getFunctionStatementBlock(namespaceKey,fname); validateFunction(dmlp, fblock); } } @@ -127,21 +129,22 @@ public void validateParseTree(DMLProgram dmlp, boolean inclFuns) { // handle regular blocks -- "main" program VariableSet vs = new VariableSet(); HashMap constVars = new HashMap<>(); - for(int i = 0; i < dmlp.getNumStatementBlocks(); i++) { + for (int i = 0; i < dmlp.getNumStatementBlocks(); i++) { StatementBlock sb = dmlp.getStatementBlock(i); vs = sb.validate(dmlp, vs, constVars, fWriteRead); constVars = sb.getConstOut(); } - // STEP3: Post-processing steps after validate - e.g., prepare read-after-write meta data - if(fWriteRead) { - // propagate size and datatypes into read + //STEP3: Post-processing steps after validate - e.g., prepare read-after-write meta data + if( fWriteRead ) + { + //propagate size and datatypes into read prepareReadAfterWrite(dmlp, new HashMap<>()); - // re-validate main program for datatype propagation + //re-validate main program for datatype propagation vs = new VariableSet(); constVars = new HashMap<>(); - for(int i = 0; i < dmlp.getNumStatementBlocks(); i++) { + for (int i = 0; i < dmlp.getNumStatementBlocks(); i++) { StatementBlock sb = dmlp.getStatementBlock(i); vs = sb.validate(dmlp, vs, constVars, fWriteRead); constVars = sb.getConstOut(); @@ -158,9 +161,9 @@ public void validateFunction(DMLProgram dmlp, FunctionStatementBlock fsb, boolea VariableSet vs = new VariableSet(); // add the input variables for the function to input variable list - FunctionStatement fstmt = (FunctionStatement) fsb.getStatement(0); - for(DataIdentifier currVar : fstmt.getInputParams()) { - if(currVar.getDataType() == DataType.SCALAR) + FunctionStatement fstmt = (FunctionStatement)fsb.getStatement(0); + for (DataIdentifier currVar : fstmt.getInputParams()) { + if (currVar.getDataType() == DataType.SCALAR) currVar.setDimensions(0, 0); vs.addVariable(currVar.getName(), currVar); } @@ -174,31 +177,31 @@ public void liveVariableAnalysis(DMLProgram dmlp) { public void liveVariableAnalysis(DMLProgram dmlp, boolean inclFuns) { // for each namespace, handle function statement blocks - if(inclFuns) { - for(String namespaceKey : dmlp.getNamespaces().keySet()) { - for(String fname : dmlp.getFunctionStatementBlocks(namespaceKey).keySet()) { + if( inclFuns ) { + for (String namespaceKey : dmlp.getNamespaces().keySet()) { + for (String fname: dmlp.getFunctionStatementBlocks(namespaceKey).keySet()) { FunctionStatementBlock fsb = dmlp.getFunctionStatementBlock(namespaceKey, fname); liveVariableAnalysisFunction(dmlp, fsb); } } } - // handle regular program blocks + // handle regular program blocks VariableSet currentLiveOut = new VariableSet(); VariableSet activeIn = new VariableSet(); // handle function inlining dmlp.setStatementBlocks(StatementBlock.mergeFunctionCalls(dmlp.getStatementBlocks(), dmlp)); - for(int i = 0; i < dmlp.getNumStatementBlocks(); i++) { + for (int i = 0; i < dmlp.getNumStatementBlocks(); i++) { StatementBlock sb = dmlp.getStatementBlock(i); activeIn = sb.initializeforwardLV(activeIn); } - if(dmlp.getNumStatementBlocks() > 0) { + if (dmlp.getNumStatementBlocks() > 0){ StatementBlock lastSb = dmlp.getStatementBlock(dmlp.getNumStatementBlocks() - 1); lastSb._liveOut = new VariableSet(); - for(int i = dmlp.getNumStatementBlocks() - 1; i >= 0; i--) { + for (int i = dmlp.getNumStatementBlocks() - 1; i >= 0; i--) { StatementBlock sb = dmlp.getStatementBlock(i); currentLiveOut = sb.analyze(currentLiveOut); } @@ -208,27 +211,27 @@ public void liveVariableAnalysis(DMLProgram dmlp, boolean inclFuns) { } public void liveVariableAnalysisFunction(DMLProgram dmlp, FunctionStatementBlock fsb) { - // STEP 1: forward direction - FunctionStatement fstmt = (FunctionStatement) fsb.getStatement(0); + //STEP 1: forward direction + FunctionStatement fstmt = (FunctionStatement)fsb.getStatement(0); // perform function inlining fstmt.setBody(StatementBlock.mergeFunctionCalls(fstmt.getBody(), dmlp)); VariableSet activeIn = new VariableSet(); - for(DataIdentifier id : fstmt.getInputParams()) { - activeIn.addVariable(id.getName(), id); + for (DataIdentifier id : fstmt.getInputParams()){ + activeIn.addVariable(id.getName(), id); } fsb.initializeforwardLV(activeIn); - // STEP 2: backward direction + //STEP 2: backward direction VariableSet currentLiveOut = new VariableSet(); VariableSet currentLiveIn = new VariableSet(); VariableSet unionLiveIn = new VariableSet(); - for(DataIdentifier id : fstmt.getInputParams()) + for (DataIdentifier id : fstmt.getInputParams()) currentLiveIn.addVariable(id.getName(), id); - for(DataIdentifier id : fstmt.getOutputParams()) { + for (DataIdentifier id : fstmt.getOutputParams()) { currentLiveOut.addVariable(id.getName(), id); unionLiveIn.addVariable(id.getName(), id); } @@ -239,13 +242,14 @@ public void liveVariableAnalysisFunction(DMLProgram dmlp, FunctionStatementBlock } public void cleanupLiveOutVariables(List sbs, VariableSet unionLiveIn) { - // backwards pass to collect union of livein variables of all successors - // and cleanup unnecessary liveout variables - for(int i = sbs.size() - 1; i >= 0; i--) { + //backwards pass to collect union of livein variables of all successors + //and cleanup unnecessary liveout variables + for(int i=sbs.size()-1; i>=0; i--) { StatementBlock sb = sbs.get(i); - // remove liveout variables that are not in unionLivein - sb.liveOut().removeVariables(VariableSet.minus(sb.liveOut(), unionLiveIn)); - // collect all livein information + //remove liveout variables that are not in unionLivein + sb.liveOut().removeVariables( + VariableSet.minus(sb.liveOut(), unionLiveIn)); + //collect all livein information unionLiveIn.addVariables(sb.liveIn()); } } @@ -256,54 +260,56 @@ public void constructHops(DMLProgram dmlp) { public void constructHops(DMLProgram dmlp, boolean inclFuns) { // Step 1: construct hops for all functions - if(inclFuns) { + if( inclFuns ) { // for each namespace, handle function program blocks - for(FunctionDictionary fdict : dmlp.getNamespaces().values()) - for(FunctionStatementBlock fsb : fdict.getFunctions().values()) + for( FunctionDictionary fdict : dmlp.getNamespaces().values() ) + for( FunctionStatementBlock fsb : fdict.getFunctions().values() ) constructHops(fsb); } // Step 2: construct hops for main program // handle regular program blocks - for(int i = 0; i < dmlp.getNumStatementBlocks(); i++) { + for (int i = 0; i < dmlp.getNumStatementBlocks(); i++) { StatementBlock current = dmlp.getStatementBlock(i); constructHops(current); } } - public void rewriteHopsDAG(DMLProgram dmlp) { - // apply hop rewrites (static rewrites) + public void rewriteHopsDAG(DMLProgram dmlp) + { + //apply hop rewrites (static rewrites) ProgramRewriter rewriter = new ProgramRewriter(true, false); - rewriter.rewriteProgramHopDAGs(dmlp, false); // rewrite and merge + rewriter.rewriteProgramHopDAGs(dmlp, false); //rewrite and merge resetHopsDAGVisitStatus(dmlp); - rewriter.rewriteProgramHopDAGs(dmlp, true); // rewrite and split + rewriter.rewriteProgramHopDAGs(dmlp, true); //rewrite and split resetHopsDAGVisitStatus(dmlp); - // propagate size information from main into functions (but conservatively) - if(OptimizerUtils.ALLOW_INTER_PROCEDURAL_ANALYSIS) { + //propagate size information from main into functions (but conservatively) + if( OptimizerUtils.ALLOW_INTER_PROCEDURAL_ANALYSIS ) { InterProceduralAnalysis ipa = new InterProceduralAnalysis(dmlp); ipa.analyzeProgram(OptimizerUtils.IPA_NUM_REPETITIONS); resetHopsDAGVisitStatus(dmlp); } - // apply hop rewrites (dynamic rewrites, after IPA) + //apply hop rewrites (dynamic rewrites, after IPA) ProgramRewriter rewriter2 = new ProgramRewriter(false, true); rewriter2.rewriteProgramHopDAGs(dmlp); resetHopsDAGVisitStatus(dmlp); - // compute memory estimates for all the hops. These estimates are used - // subsequently in various optimizations, e.g. CP vs. MR scheduling and parfor. + //compute memory estimates for all the hops. These estimates are used + //subsequently in various optimizations, e.g. CP vs. MR scheduling and parfor. refreshMemEstimates(dmlp); resetHopsDAGVisitStatus(dmlp); - // enhance HOP DAGs by automatic operator fusion + //enhance HOP DAGs by automatic operator fusion DMLConfig dmlconf = ConfigurationManager.getDMLConfig(); - if(ConfigurationManager.isCodegenEnabled()) { - SpoofCompiler.PLAN_CACHE_POLICY = PlanCachePolicy.get(dmlconf.getBooleanValue(DMLConfig.CODEGEN_PLANCACHE), - dmlconf.getIntValue(DMLConfig.CODEGEN_LITERALS) == 2); + if( ConfigurationManager.isCodegenEnabled() ){ + SpoofCompiler.PLAN_CACHE_POLICY = PlanCachePolicy.get( + dmlconf.getBooleanValue(DMLConfig.CODEGEN_PLANCACHE), + dmlconf.getIntValue(DMLConfig.CODEGEN_LITERALS)==2); SpoofCompiler.setConfiguredPlanSelector(); SpoofCompiler.setExecTypeSpecificJavaCompiler(); - if(SpoofCompiler.INTEGRATION == IntegrationType.HOPS) + if( SpoofCompiler.INTEGRATION==IntegrationType.HOPS ) codgenHopsDAG(dmlp); } } @@ -327,31 +333,33 @@ public void codgenHopsDAG(ProgramBlock pb) { public void constructLops(DMLProgram dmlp) { // for each namespace, handle function program blocks - for(FunctionDictionary fdict : dmlp.getNamespaces().values()) { - // handle optimized functions - for(FunctionStatementBlock fsb : fdict.getFunctions().values()) + for( FunctionDictionary fdict : dmlp.getNamespaces().values() ) { + //handle optimized functions + for( FunctionStatementBlock fsb : fdict.getFunctions().values() ) constructLops(fsb); - // handle unoptimized functions - if(fdict.getFunctions(false) != null) - for(FunctionStatementBlock fsb : fdict.getFunctions(false).values()) + //handle unoptimized functions + if( fdict.getFunctions(false) != null ) + for( FunctionStatementBlock fsb : fdict.getFunctions(false).values() ) constructLops(fsb); } // handle regular program blocks - for(StatementBlock sb : dmlp.getStatementBlocks()) + for( StatementBlock sb : dmlp.getStatementBlocks() ) constructLops(sb); } - public boolean constructLops(StatementBlock sb) { + public boolean constructLops(StatementBlock sb) + { boolean ret = false; - if(sb instanceof WhileStatementBlock) { - WhileStatementBlock wsb = (WhileStatementBlock) sb; - WhileStatement whileStmt = (WhileStatement) wsb.getStatement(0); + if (sb instanceof WhileStatementBlock) + { + WhileStatementBlock wsb = (WhileStatementBlock)sb; + WhileStatement whileStmt = (WhileStatement)wsb.getStatement(0); ArrayList body = whileStmt.getBody(); // step through stmt blocks in while stmt body - for(StatementBlock stmtBlock : body) + for (StatementBlock stmtBlock : body) ret |= constructLops(stmtBlock); // handle while stmt predicate @@ -360,18 +368,19 @@ public boolean constructLops(StatementBlock sb) { ret |= wsb.updatePredicateRecompilationFlag(); } - else if(sb instanceof IfStatementBlock) { + else if (sb instanceof IfStatementBlock) + { IfStatementBlock isb = (IfStatementBlock) sb; - IfStatement ifStmt = (IfStatement) isb.getStatement(0); + IfStatement ifStmt = (IfStatement)isb.getStatement(0); ArrayList ifBody = ifStmt.getIfBody(); ArrayList elseBody = ifStmt.getElseBody(); // step through stmt blocks in if stmt ifBody - for(StatementBlock stmtBlock : ifBody) + for (StatementBlock stmtBlock : ifBody) ret |= constructLops(stmtBlock); // step through stmt blocks in if stmt elseBody - for(StatementBlock stmtBlock : elseBody) + for (StatementBlock stmtBlock : elseBody) ret |= constructLops(stmtBlock); // handle if stmt predicate @@ -380,49 +389,49 @@ else if(sb instanceof IfStatementBlock) { ret |= isb.updatePredicateRecompilationFlag(); } - else if(sb instanceof ForStatementBlock) // NOTE: applies to ForStatementBlock and ParForStatementBlock + else if (sb instanceof ForStatementBlock) //NOTE: applies to ForStatementBlock and ParForStatementBlock { - ForStatementBlock fsb = (ForStatementBlock) sb; - ForStatement fs = (ForStatement) sb.getStatement(0); + ForStatementBlock fsb = (ForStatementBlock) sb; + ForStatement fs = (ForStatement)sb.getStatement(0); ArrayList body = fs.getBody(); // step through stmt blocks in FOR stmt body - for(StatementBlock stmtBlock : body) + for (StatementBlock stmtBlock : body) ret |= constructLops(stmtBlock); // handle for stmt predicate - if(fsb.getFromHops() != null) { + if (fsb.getFromHops() != null){ Lop llobs = fsb.getFromHops().constructLops(); fsb.setFromLops(llobs); } - if(fsb.getToHops() != null) { + if (fsb.getToHops() != null){ Lop llobs = fsb.getToHops().constructLops(); fsb.setToLops(llobs); } - if(fsb.getIncrementHops() != null) { + if (fsb.getIncrementHops() != null){ Lop llobs = fsb.getIncrementHops().constructLops(); fsb.setIncrementLops(llobs); } ret |= fsb.updatePredicateRecompilationFlags(); } - else if(sb instanceof FunctionStatementBlock) { + else if (sb instanceof FunctionStatementBlock) { FunctionStatementBlock fsb = (FunctionStatementBlock) sb; - FunctionStatement functStmt = (FunctionStatement) sb.getStatement(0); + FunctionStatement functStmt = (FunctionStatement)sb.getStatement(0); ArrayList body = functStmt.getBody(); // step through stmt blocks in while stmt body - for(StatementBlock stmtBlock : body) + for( StatementBlock stmtBlock : body ) ret |= constructLops(stmtBlock); - if(fsb.isRecompileOnce()) + if( fsb.isRecompileOnce() ) fsb.setRecompileOnce(ret); } // handle default case for regular StatementBlock else { - if(sb.getHops() == null) + if (sb.getHops() == null) sb.setHops(new ArrayList()); ArrayList lops = new ArrayList<>(); - for(Hop hop : sb.getHops()) + for (Hop hop : sb.getHops()) lops.add(hop.constructLops()); sb.setLops(lops); ret |= sb.updateRecompilationFlag(); @@ -431,20 +440,21 @@ else if(sb instanceof FunctionStatementBlock) { return ret; } - public Program getRuntimeProgram(DMLProgram prog, DMLConfig config) - throws LanguageException, DMLRuntimeException, LopsException, HopsException { + public Program getRuntimeProgram(DMLProgram prog, DMLConfig config) + throws LanguageException, DMLRuntimeException, LopsException, HopsException + { // constructor resets the set of registered functions Program rtprog = new Program(prog); // for all namespaces, translate function statement blocks into function program blocks - for(String namespace : prog.getNamespaces().keySet()) { + for (String namespace : prog.getNamespaces().keySet()){ - for(String fname : prog.getFunctionStatementBlocks(namespace).keySet()) { + for (String fname : prog.getFunctionStatementBlocks(namespace).keySet()){ // add program block to program FunctionStatementBlock fsb = prog.getFunctionStatementBlocks(namespace).get(fname); prepareAndAddFunctionProgramBlock(rtprog, config, namespace, fname, fsb, true); // add unoptimized block to program (for second-order calls) - if(prog.getNamespaces().get(namespace).containsFunction(fname, false)) { + if( prog.getNamespaces().get(namespace).containsFunction(fname, false) ) { prepareAndAddFunctionProgramBlock(rtprog, config, namespace, fname, prog.getNamespaces().get(namespace).getFunction(fname, false), false); } @@ -452,23 +462,25 @@ public Program getRuntimeProgram(DMLProgram prog, DMLConfig config) } // translate all top-level statement blocks to program blocks - for(StatementBlock sb : prog.getStatementBlocks()) { + for (StatementBlock sb : prog.getStatementBlocks() ) { // add program block to program ProgramBlock rtpb = createRuntimeProgramBlock(rtprog, sb, config); rtprog.addProgramBlock(rtpb); } - // enhance runtime program by automatic operator fusion - if(ConfigurationManager.isCodegenEnabled() && SpoofCompiler.INTEGRATION == IntegrationType.RUNTIME) { + //enhance runtime program by automatic operator fusion + if( ConfigurationManager.isCodegenEnabled() + && SpoofCompiler.INTEGRATION==IntegrationType.RUNTIME ){ codgenHopsDAG(rtprog); } - return rtprog; + return rtprog ; } - private void prepareAndAddFunctionProgramBlock(Program rtprog, DMLConfig config, String fnamespace, String fname, - FunctionStatementBlock fsb, boolean opt) { - FunctionProgramBlock rtpb = (FunctionProgramBlock) createRuntimeProgramBlock(rtprog, fsb, config); + private void prepareAndAddFunctionProgramBlock(Program rtprog, DMLConfig config, + String fnamespace, String fname, FunctionStatementBlock fsb, boolean opt) + { + FunctionProgramBlock rtpb = (FunctionProgramBlock)createRuntimeProgramBlock(rtprog, fsb, config); rtprog.addFunctionProgramBlock(fnamespace, fname, rtpb, opt); rtpb.setRecompileOnce(fsb.isRecompileOnce()); rtpb.setNondeterministic(fsb.isNondeterministic()); @@ -484,7 +496,7 @@ public ProgramBlock createRuntimeProgramBlock(Program prog, StatementBlock sb, D ProgramBlock retPB = null; // process While Statement - add runtime program blocks to program - if(sb instanceof WhileStatementBlock) { + if (sb instanceof WhileStatementBlock){ // create DAG for loop predicates pred_dag = new Dag<>(); @@ -493,7 +505,7 @@ public ProgramBlock createRuntimeProgramBlock(Program prog, StatementBlock sb, D // create instructions for loop predicates pred_instruct = new ArrayList<>(); ArrayList pInst = pred_dag.getJobs(null, config); - for(Instruction i : pInst) { + for (Instruction i : pInst ) { pred_instruct.add(i); } @@ -502,9 +514,9 @@ public ProgramBlock createRuntimeProgramBlock(Program prog, StatementBlock sb, D //// process the body of the while statement block //// - WhileStatementBlock wsb = (WhileStatementBlock) sb; - WhileStatement wstmt = (WhileStatement) wsb.getStatement(0); - for(StatementBlock sblock : wstmt.getBody()) { + WhileStatementBlock wsb = (WhileStatementBlock)sb; + WhileStatement wstmt = (WhileStatement)wsb.getStatement(0); + for (StatementBlock sblock : wstmt.getBody()){ // process the body ProgramBlock childBlock = createRuntimeProgramBlock(prog, sblock, config); @@ -513,7 +525,7 @@ public ProgramBlock createRuntimeProgramBlock(Program prog, StatementBlock sb, D retPB = rtpb; - // post processing for generating missing instructions + //post processing for generating missing instructions retPB.setExitInstruction(deriveExitInstruction(sb)); // add statement block @@ -524,7 +536,7 @@ public ProgramBlock createRuntimeProgramBlock(Program prog, StatementBlock sb, D } // process If Statement - add runtime program blocks to program - else if(sb instanceof IfStatementBlock) { + else if (sb instanceof IfStatementBlock){ // create DAG for loop predicates pred_dag = new Dag<>(); @@ -533,7 +545,7 @@ else if(sb instanceof IfStatementBlock) { // create instructions for loop predicates pred_instruct = new ArrayList<>(); ArrayList pInst = pred_dag.getJobs(null, config); - for(Instruction i : pInst) { + for (Instruction i : pInst ) { pred_instruct.add(i); } @@ -541,24 +553,24 @@ else if(sb instanceof IfStatementBlock) { IfProgramBlock rtpb = new IfProgramBlock(prog, pred_instruct); // process the body of the if statement block - IfStatementBlock isb = (IfStatementBlock) sb; - IfStatement istmt = (IfStatement) isb.getStatement(0); + IfStatementBlock isb = (IfStatementBlock)sb; + IfStatement istmt = (IfStatement)isb.getStatement(0); // process the if body - for(StatementBlock sblock : istmt.getIfBody()) { + for (StatementBlock sblock : istmt.getIfBody()){ ProgramBlock childBlock = createRuntimeProgramBlock(prog, sblock, config); rtpb.addProgramBlockIfBody(childBlock); } // process the else body - for(StatementBlock sblock : istmt.getElseBody()) { + for (StatementBlock sblock : istmt.getElseBody()){ ProgramBlock childBlock = createRuntimeProgramBlock(prog, sblock, config); - rtpb.addProgramBlockElseBody(childBlock); + rtpb.addProgramBlockElseBody(childBlock); } retPB = rtpb; - // post processing for generating missing instructions + //post processing for generating missing instructions retPB.setExitInstruction(deriveExitInstruction(sb)); // add statement block @@ -570,18 +582,19 @@ else if(sb instanceof IfStatementBlock) { // process For Statement - add runtime program blocks to program // NOTE: applies to ForStatementBlock and ParForStatementBlock - else if(sb instanceof ForStatementBlock) { + else if (sb instanceof ForStatementBlock) + { ForStatementBlock fsb = (ForStatementBlock) sb; - // create DAGs for loop predicates + // create DAGs for loop predicates Dag fromDag = new Dag<>(); Dag toDag = new Dag<>(); Dag incrementDag = new Dag<>(); - if(fsb.getFromHops() != null) + if( fsb.getFromHops()!=null ) fsb.getFromLops().addToDag(fromDag); - if(fsb.getToHops() != null) + if( fsb.getToHops()!=null ) fsb.getToLops().addToDag(toDag); - if(fsb.getIncrementHops() != null) + if( fsb.getIncrementHops()!=null ) fsb.getIncrementLops().addToDag(incrementDag); // create instructions for loop predicates @@ -593,13 +606,13 @@ else if(sb instanceof ForStatementBlock) { ForProgramBlock rtpb = null; IterablePredicate iterPred = fsb.getIterPredicate(); - if(sb instanceof ParForStatementBlock && ConfigurationManager.isParallelParFor()) { - rtpb = new ParForProgramBlock(prog, iterPred.getIterVar().getName(), iterPred.getParForParams(), - ((ParForStatementBlock) sb).getResultVariables()); - ParForProgramBlock pfrtpb = (ParForProgramBlock) rtpb; - pfrtpb.setStatementBlock(sb); // used for optimization and creating unscoped variables + if( sb instanceof ParForStatementBlock && ConfigurationManager.isParallelParFor() ) { + rtpb = new ParForProgramBlock(prog, iterPred.getIterVar().getName(), + iterPred.getParForParams(), ((ParForStatementBlock)sb).getResultVariables()); + ParForProgramBlock pfrtpb = (ParForProgramBlock)rtpb; + pfrtpb.setStatementBlock(sb); //used for optimization and creating unscoped variables } - else {// ForStatementBlock + else {//ForStatementBlock rtpb = new ForProgramBlock(prog, iterPred.getIterVar().getName()); } @@ -608,15 +621,15 @@ else if(sb instanceof ForStatementBlock) { rtpb.setIncrementInstructions(incrementInstructions); // process the body of the for statement block - ForStatement fs = (ForStatement) fsb.getStatement(0); - for(StatementBlock sblock : fs.getBody()) { + ForStatement fs = (ForStatement)fsb.getStatement(0); + for (StatementBlock sblock : fs.getBody()){ ProgramBlock childBlock = createRuntimeProgramBlock(prog, sblock, config); - rtpb.addProgramBlock(childBlock); + rtpb.addProgramBlock(childBlock); } retPB = rtpb; - // post processing for generating missing instructions + //post processing for generating missing instructions retPB.setExitInstruction(deriveExitInstruction(sb)); // add statement block @@ -627,24 +640,24 @@ else if(sb instanceof ForStatementBlock) { } // process function statement block - add runtime program blocks to program - else if(sb instanceof FunctionStatementBlock) { + else if (sb instanceof FunctionStatementBlock){ - FunctionStatementBlock fsb = (FunctionStatementBlock) sb; - FunctionStatement fstmt = (FunctionStatement) fsb.getStatement(0); + FunctionStatementBlock fsb = (FunctionStatementBlock)sb; + FunctionStatement fstmt = (FunctionStatement)fsb.getStatement(0); FunctionProgramBlock rtpb = null; // create function program block rtpb = new FunctionProgramBlock(prog, fstmt.getInputParams(), fstmt.getOutputParams()); // process the function statement body - for(StatementBlock sblock : fstmt.getBody()) { + for (StatementBlock sblock : fstmt.getBody()){ // process the body ProgramBlock childBlock = createRuntimeProgramBlock(prog, sblock, config); rtpb.addProgramBlock(childBlock); } // check there are actually Lops in to process (loop stmt body will not have any) - if(fsb.getLops() != null && !fsb.getLops().isEmpty()) { + if (fsb.getLops() != null && !fsb.getLops().isEmpty()){ throw new LopsException(fsb.printBlockErrorLocation() + "FunctionStatementBlock should have no Lops"); } @@ -665,9 +678,9 @@ else if(sb instanceof FunctionStatementBlock) { dag = new Dag<>(); // check there are actually Lops in to process (loop stmt body will not have any) - if(sb.getLops() != null && !sb.getLops().isEmpty()) { + if (sb.getLops() != null && !sb.getLops().isEmpty()){ - for(Lop l : sb.getLops()) { + for (Lop l : sb.getLops()) { l.addToDag(dag); } @@ -678,8 +691,8 @@ else if(sb instanceof FunctionStatementBlock) { retPB = rtpb; - // post processing for generating missing instructions - // retPB.setExitInstruction(deriveExitInstruction(sb)); + //post processing for generating missing instructions + //retPB.setExitInstruction(deriveExitInstruction(sb)); // add statement block retPB.setStatementBlock(sb); @@ -694,89 +707,90 @@ else if(sb instanceof FunctionStatementBlock) { public static void refreshMemEstimates(DMLProgram dmlp) { // for each namespace, handle function program blocks -- forward direction - for(String namespaceKey : dmlp.getNamespaces().keySet()) { - for(String fname : dmlp.getFunctionStatementBlocks(namespaceKey).keySet()) { + for (String namespaceKey : dmlp.getNamespaces().keySet()){ + for (String fname : dmlp.getFunctionStatementBlocks(namespaceKey).keySet()){ FunctionStatementBlock fsblock = dmlp.getFunctionStatementBlock(namespaceKey, fname); refreshMemEstimates(fsblock); } } // handle statement blocks in "main" method - for(int i = 0; i < dmlp.getNumStatementBlocks(); i++) { + for (int i = 0; i < dmlp.getNumStatementBlocks(); i++) { StatementBlock current = dmlp.getStatementBlock(i); refreshMemEstimates(current); } } private static Instruction deriveExitInstruction(StatementBlock sb) { - Set rmVars = VariableSet - .union(VariableSet.minus(sb.liveIn(), sb.liveOut()), VariableSet.minus(sb.getKill(), sb.liveOut())) - .getVariableNames(); - return rmVars.isEmpty() ? null : VariableCPInstruction.prepareRemoveInstruction(rmVars.toArray(new String[0])); + Set rmVars = VariableSet.union( + VariableSet.minus(sb.liveIn(), sb.liveOut()), + VariableSet.minus(sb.getKill(), sb.liveOut())).getVariableNames(); + return rmVars.isEmpty() ? null : + VariableCPInstruction.prepareRemoveInstruction(rmVars.toArray(new String[0])); } public static void refreshMemEstimates(StatementBlock current) { MemoTable memo = new MemoTable(); - if(HopRewriteUtils.isLastLevelStatementBlock(current)) { + if( HopRewriteUtils.isLastLevelStatementBlock(current) ) { ArrayList hopsDAG = current.getHops(); - if(hopsDAG != null && !hopsDAG.isEmpty()) - for(Hop hop : hopsDAG) + if (hopsDAG != null && !hopsDAG.isEmpty()) + for( Hop hop : hopsDAG ) hop.refreshMemEstimates(memo); } - if(current instanceof FunctionStatementBlock) { + if (current instanceof FunctionStatementBlock) { - FunctionStatement fstmt = (FunctionStatement) current.getStatement(0); - for(StatementBlock sb : fstmt.getBody()) { + FunctionStatement fstmt = (FunctionStatement)current.getStatement(0); + for (StatementBlock sb : fstmt.getBody()){ refreshMemEstimates(sb); } } - else if(current instanceof WhileStatementBlock) { + else if (current instanceof WhileStatementBlock) { // handle predicate WhileStatementBlock wstb = (WhileStatementBlock) current; wstb.getPredicateHops().refreshMemEstimates(new MemoTable()); - if(wstb.getNumStatements() > 1) + if (wstb.getNumStatements() > 1) LOG.debug("While statement block has more than 1 stmt"); - WhileStatement ws = (WhileStatement) wstb.getStatement(0); + WhileStatement ws = (WhileStatement)wstb.getStatement(0); - for(StatementBlock sb : ws.getBody()) { + for (StatementBlock sb : ws.getBody()){ refreshMemEstimates(sb); } } - else if(current instanceof IfStatementBlock) { + else if (current instanceof IfStatementBlock) { // handle predicate IfStatementBlock istb = (IfStatementBlock) current; istb.getPredicateHops().refreshMemEstimates(new MemoTable()); - if(istb.getNumStatements() > 1) + if (istb.getNumStatements() > 1) LOG.debug("If statement block has more than 1 stmt"); - IfStatement is = (IfStatement) istb.getStatement(0); + IfStatement is = (IfStatement)istb.getStatement(0); - for(StatementBlock sb : is.getIfBody()) { + for (StatementBlock sb : is.getIfBody()){ refreshMemEstimates(sb); } - for(StatementBlock sb : is.getElseBody()) { + for (StatementBlock sb : is.getElseBody()){ refreshMemEstimates(sb); } } - else if(current instanceof ForStatementBlock) { + else if (current instanceof ForStatementBlock) { // handle predicate ForStatementBlock fsb = (ForStatementBlock) current; - if(fsb.getFromHops() != null) + if (fsb.getFromHops() != null) fsb.getFromHops().refreshMemEstimates(new MemoTable()); - if(fsb.getToHops() != null) + if (fsb.getToHops() != null) fsb.getToHops().refreshMemEstimates(new MemoTable()); - if(fsb.getIncrementHops() != null) + if (fsb.getIncrementHops() != null) fsb.getIncrementHops().refreshMemEstimates(new MemoTable()); - if(fsb.getNumStatements() > 1) + if (fsb.getNumStatements() > 1) LOG.debug("For statement block has more than 1 stmt"); - ForStatement ws = (ForStatement) fsb.getStatement(0); + ForStatement ws = (ForStatement)fsb.getStatement(0); - for(StatementBlock sb : ws.getBody()) { + for (StatementBlock sb : ws.getBody()){ refreshMemEstimates(sb); } } @@ -785,15 +799,15 @@ else if(current instanceof ForStatementBlock) { public static void resetHopsDAGVisitStatus(DMLProgram dmlp) { // for each namespace, handle function program blocks -- forward direction - for(String namespaceKey : dmlp.getNamespaces().keySet()) { - for(String fname : dmlp.getFunctionStatementBlocks(namespaceKey).keySet()) { + for (String namespaceKey : dmlp.getNamespaces().keySet()){ + for (String fname : dmlp.getFunctionStatementBlocks(namespaceKey).keySet()){ FunctionStatementBlock fsblock = dmlp.getFunctionStatementBlock(namespaceKey, fname); resetHopsDAGVisitStatus(fsblock); } } // handle statement blocks in "main" method - for(int i = 0; i < dmlp.getNumStatementBlocks(); i++) { + for (int i = 0; i < dmlp.getNumStatementBlocks(); i++) { StatementBlock current = dmlp.getStatementBlock(i); resetHopsDAGVisitStatus(current); } @@ -801,54 +815,54 @@ public static void resetHopsDAGVisitStatus(DMLProgram dmlp) { public static void resetHopsDAGVisitStatus(StatementBlock current) { - if(HopRewriteUtils.isLastLevelStatementBlock(current)) { + if( HopRewriteUtils.isLastLevelStatementBlock(current) ) { ArrayList hopsDAG = current.getHops(); - if(hopsDAG != null && !hopsDAG.isEmpty()) { + if (hopsDAG != null && !hopsDAG.isEmpty() ) { Hop.resetVisitStatus(hopsDAG); } } - if(current instanceof FunctionStatementBlock) { - FunctionStatement fstmt = (FunctionStatement) current.getStatement(0); - for(StatementBlock sb : fstmt.getBody()) { + if (current instanceof FunctionStatementBlock) { + FunctionStatement fstmt = (FunctionStatement)current.getStatement(0); + for (StatementBlock sb : fstmt.getBody()){ resetHopsDAGVisitStatus(sb); } } - else if(current instanceof WhileStatementBlock) { + else if (current instanceof WhileStatementBlock) { // handle predicate WhileStatementBlock wstb = (WhileStatementBlock) current; wstb.getPredicateHops().resetVisitStatus(); - WhileStatement ws = (WhileStatement) wstb.getStatement(0); - for(StatementBlock sb : ws.getBody()) + WhileStatement ws = (WhileStatement)wstb.getStatement(0); + for (StatementBlock sb : ws.getBody()) resetHopsDAGVisitStatus(sb); } - else if(current instanceof IfStatementBlock) { + else if (current instanceof IfStatementBlock) { // handle predicate IfStatementBlock istb = (IfStatementBlock) current; istb.getPredicateHops().resetVisitStatus(); - IfStatement is = (IfStatement) istb.getStatement(0); - for(StatementBlock sb : is.getIfBody()) + IfStatement is = (IfStatement)istb.getStatement(0); + for (StatementBlock sb : is.getIfBody()) resetHopsDAGVisitStatus(sb); - for(StatementBlock sb : is.getElseBody()) + for (StatementBlock sb : is.getElseBody()) resetHopsDAGVisitStatus(sb); } - else if(current instanceof ForStatementBlock) { + else if (current instanceof ForStatementBlock) { // handle predicate ForStatementBlock fsb = (ForStatementBlock) current; - if(fsb.getFromHops() != null) + if (fsb.getFromHops() != null) fsb.getFromHops().resetVisitStatus(); - if(fsb.getToHops() != null) + if (fsb.getToHops() != null) fsb.getToHops().resetVisitStatus(); - if(fsb.getIncrementHops() != null) + if (fsb.getIncrementHops() != null) fsb.getIncrementHops().resetVisitStatus(); - if(fsb.getNumStatements() > 1) + if (fsb.getNumStatements() > 1) LOG.debug("For statment block has more than 1 stmt"); - ForStatement ws = (ForStatement) fsb.getStatement(0); + ForStatement ws = (ForStatement)fsb.getStatement(0); - for(StatementBlock sb : ws.getBody()) { + for (StatementBlock sb : ws.getBody()){ resetHopsDAGVisitStatus(sb); } } @@ -857,14 +871,14 @@ else if(current instanceof ForStatementBlock) { public void resetLopsDAGVisitStatus(DMLProgram dmlp) { // for each namespace, handle function program blocks - for(String namespaceKey : dmlp.getNamespaces().keySet()) { - for(String fname : dmlp.getFunctionStatementBlocks(namespaceKey).keySet()) { + for (String namespaceKey : dmlp.getNamespaces().keySet()){ + for (String fname : dmlp.getFunctionStatementBlocks(namespaceKey).keySet()){ FunctionStatementBlock fsblock = dmlp.getFunctionStatementBlock(namespaceKey, fname); resetLopsDAGVisitStatus(fsblock); } } - for(int i = 0; i < dmlp.getNumStatementBlocks(); i++) { + for (int i = 0; i < dmlp.getNumStatementBlocks(); i++) { StatementBlock current = dmlp.getStatementBlock(i); resetLopsDAGVisitStatus(current); } @@ -874,88 +888,88 @@ public void resetLopsDAGVisitStatus(StatementBlock current) { ArrayList hopsDAG = current.getHops(); - if(hopsDAG != null && !hopsDAG.isEmpty()) { + if (hopsDAG != null && !hopsDAG.isEmpty() ) { Iterator iter = hopsDAG.iterator(); - while(iter.hasNext()) { + while (iter.hasNext()){ Hop currentHop = iter.next(); currentHop.getLops().resetVisitStatus(); } } - if(current instanceof FunctionStatementBlock) { + if (current instanceof FunctionStatementBlock) { FunctionStatementBlock fsb = (FunctionStatementBlock) current; - FunctionStatement fs = (FunctionStatement) fsb.getStatement(0); + FunctionStatement fs = (FunctionStatement)fsb.getStatement(0); - for(StatementBlock sb : fs.getBody()) { + for (StatementBlock sb : fs.getBody()){ resetLopsDAGVisitStatus(sb); } } - if(current instanceof WhileStatementBlock) { + if (current instanceof WhileStatementBlock) { WhileStatementBlock wstb = (WhileStatementBlock) current; wstb.getPredicateLops().resetVisitStatus(); - if(wstb.getNumStatements() > 1) + if (wstb.getNumStatements() > 1) LOG.debug("While statement block has more than 1 stmt"); - WhileStatement ws = (WhileStatement) wstb.getStatement(0); + WhileStatement ws = (WhileStatement)wstb.getStatement(0); - for(StatementBlock sb : ws.getBody()) { + for (StatementBlock sb : ws.getBody()){ resetLopsDAGVisitStatus(sb); } } - if(current instanceof IfStatementBlock) { + if (current instanceof IfStatementBlock) { IfStatementBlock istb = (IfStatementBlock) current; istb.getPredicateLops().resetVisitStatus(); - if(istb.getNumStatements() > 1) + if (istb.getNumStatements() > 1) LOG.debug("If statement block has more than 1 stmt"); - IfStatement is = (IfStatement) istb.getStatement(0); + IfStatement is = (IfStatement)istb.getStatement(0); - for(StatementBlock sb : is.getIfBody()) { + for (StatementBlock sb : is.getIfBody()){ resetLopsDAGVisitStatus(sb); } - for(StatementBlock sb : is.getElseBody()) { + for (StatementBlock sb : is.getElseBody()){ resetLopsDAGVisitStatus(sb); } } - if(current instanceof ForStatementBlock) { + if (current instanceof ForStatementBlock) { ForStatementBlock fsb = (ForStatementBlock) current; - if(fsb.getFromLops() != null) + if (fsb.getFromLops() != null) fsb.getFromLops().resetVisitStatus(); - if(fsb.getToLops() != null) + if (fsb.getToLops() != null) fsb.getToLops().resetVisitStatus(); - if(fsb.getIncrementLops() != null) + if (fsb.getIncrementLops() != null) fsb.getIncrementLops().resetVisitStatus(); - if(fsb.getNumStatements() > 1) + if (fsb.getNumStatements() > 1) LOG.debug("For statement block has more than 1 stmt"); - ForStatement ws = (ForStatement) fsb.getStatement(0); + ForStatement ws = (ForStatement)fsb.getStatement(0); - for(StatementBlock sb : ws.getBody()) { + for (StatementBlock sb : ws.getBody()){ resetLopsDAGVisitStatus(sb); } } } public void constructHops(StatementBlock sb) { - if(sb instanceof WhileStatementBlock) { + if (sb instanceof WhileStatementBlock) { constructHopsForWhileControlBlock((WhileStatementBlock) sb); return; } - if(sb instanceof IfStatementBlock) { + if (sb instanceof IfStatementBlock) { constructHopsForIfControlBlock((IfStatementBlock) sb); return; } - if(sb instanceof ForStatementBlock) { // incl ParForStatementBlock + if (sb instanceof ForStatementBlock) { //incl ParForStatementBlock constructHopsForForControlBlock((ForStatementBlock) sb); return; } - if(sb instanceof FunctionStatementBlock) { + if (sb instanceof FunctionStatementBlock) { constructHopsForFunctionControlBlock((FunctionStatementBlock) sb); return; } @@ -963,80 +977,76 @@ public void constructHops(StatementBlock sb) { HashMap ids = new HashMap<>(); ArrayList output = new ArrayList<>(); - VariableSet liveIn = sb.liveIn(); + VariableSet liveIn = sb.liveIn(); VariableSet liveOut = sb.liveOut(); VariableSet updated = sb._updated; - VariableSet gen = sb._gen; + VariableSet gen = sb._gen; VariableSet updatedLiveOut = new VariableSet(); // handle liveout variables that are updated --> target identifiers for Assignment HashMap liveOutToTemp = new HashMap<>(); - for(int i = 0; i < sb.getNumStatements(); i++) { + for (int i = 0; i < sb.getNumStatements(); i++) { Statement current = sb.getStatement(i); - if(current instanceof AssignmentStatement) { + if (current instanceof AssignmentStatement) { AssignmentStatement as = (AssignmentStatement) current; DataIdentifier target = as.getTarget(); - if(target != null) { - if(liveOut.containsVariable(target.getName())) { + if (target != null) { + if (liveOut.containsVariable(target.getName())) { liveOutToTemp.put(target.getName(), Integer.valueOf(i)); } } } - if(current instanceof MultiAssignmentStatement) { + if (current instanceof MultiAssignmentStatement) { MultiAssignmentStatement mas = (MultiAssignmentStatement) current; - for(DataIdentifier target : mas.getTargetList()) { - if(liveOut.containsVariable(target.getName())) { + for (DataIdentifier target : mas.getTargetList()){ + if (liveOut.containsVariable(target.getName())) { liveOutToTemp.put(target.getName(), Integer.valueOf(i)); } } } } - // only create transient read operations for variables either updated or read-before-update - // (i.e., from LV analysis, updated and gen sets) - if(!liveIn.getVariables().values().isEmpty()) { + // only create transient read operations for variables either updated or read-before-update + // (i.e., from LV analysis, updated and gen sets) + if ( !liveIn.getVariables().values().isEmpty() ) { - for(String varName : liveIn.getVariables().keySet()) { + for (String varName : liveIn.getVariables().keySet()) { - if(updated.containsVariable(varName) || gen.containsVariable(varName)) { + if (updated.containsVariable(varName) || gen.containsVariable(varName)){ DataIdentifier var = liveIn.getVariables().get(varName); - long actualDim1 = (var instanceof IndexedIdentifier) ? ((IndexedIdentifier) var).getOrigDim1() : var - .getDim1(); - long actualDim2 = (var instanceof IndexedIdentifier) ? ((IndexedIdentifier) var).getOrigDim2() : var - .getDim2(); - DataOp read = new DataOp(var.getName(), var.getDataType(), var.getValueType(), - OpOpData.TRANSIENTREAD, null, actualDim1, actualDim2, var.getNnz(), var.getBlocksize()); + long actualDim1 = (var instanceof IndexedIdentifier) ? ((IndexedIdentifier)var).getOrigDim1() : var.getDim1(); + long actualDim2 = (var instanceof IndexedIdentifier) ? ((IndexedIdentifier)var).getOrigDim2() : var.getDim2(); + DataOp read = new DataOp(var.getName(), var.getDataType(), var.getValueType(), OpOpData.TRANSIENTREAD, null, actualDim1, actualDim2, var.getNnz(), var.getBlocksize()); read.setParseInfo(var); ids.put(varName, read); } } } - for(int i = 0; i < sb.getNumStatements(); i++) { + for( int i = 0; i < sb.getNumStatements(); i++ ) { Statement current = sb.getStatement(i); - if(current instanceof OutputStatement) { + if (current instanceof OutputStatement) { OutputStatement os = (OutputStatement) current; DataExpression source = os.getSource(); DataIdentifier target = os.getIdentifier(); - // error handling unsupported indexing expression in write statement - if(target instanceof IndexedIdentifier) { - throw new LanguageException( - source.printErrorLocation() + ": Unsupported indexing expression in write statement. " - + "Please, assign the right indexing result to a variable and write this variable."); + //error handling unsupported indexing expression in write statement + if( target instanceof IndexedIdentifier ) { + throw new LanguageException(source.printErrorLocation()+": Unsupported indexing expression in write statement. " + + "Please, assign the right indexing result to a variable and write this variable."); } - DataOp ae = (DataOp) processExpression(source, target, ids); + DataOp ae = (DataOp)processExpression(source, target, ids); Expression fmtExpr = os.getExprParam(DataExpression.FORMAT_TYPE); - ae.setFileFormat((fmtExpr instanceof StringIdentifier) ? Expression - .convertFormatType(fmtExpr.toString()) : FileFormat.UNKNOWN); + ae.setFileFormat((fmtExpr instanceof StringIdentifier) ? + Expression.convertFormatType(fmtExpr.toString()) : FileFormat.UNKNOWN); - if(ae.getDataType() == DataType.SCALAR) { + if (ae.getDataType() == DataType.SCALAR ) { ae.setOutputParams(ae.getDim1(), ae.getDim2(), ae.getNnz(), ae.getUpdateType(), -1); } else { @@ -1053,8 +1063,7 @@ public void constructHops(StatementBlock sb) { case COMPRESSED: case UNKNOWN: // write output in binary block format - ae.setOutputParams(ae.getDim1(), ae.getDim2(), ae.getNnz(), ae.getUpdateType(), - ae.getBlocksize()); + ae.setOutputParams(ae.getDim1(), ae.getDim2(), ae.getNnz(), ae.getUpdateType(), ae.getBlocksize()); break; case FEDERATED: ae.setOutputParams(ae.getDim1(), ae.getDim2(), -1, ae.getUpdateType(), -1); @@ -1067,7 +1076,7 @@ public void constructHops(StatementBlock sb) { output.add(ae); } - if(current instanceof PrintStatement) { + if (current instanceof PrintStatement) { DataIdentifier target = createTarget(); target.setDataType(DataType.SCALAR); target.setValueType(ValueType.STRING); @@ -1077,72 +1086,66 @@ public void constructHops(StatementBlock sb) { PRINTTYPE ptype = ps.getType(); try { - if(ptype == PRINTTYPE.PRINT) { + if (ptype == PRINTTYPE.PRINT) { OpOp1 op = OpOp1.PRINT; Expression source = ps.getExpressions().get(0); Hop ae = processExpression(source, target, ids); - Hop printHop = new UnaryOp(target.getName(), target.getDataType(), target.getValueType(), op, - ae); + Hop printHop = new UnaryOp(target.getName(), target.getDataType(), target.getValueType(), op, ae); printHop.setParseInfo(current); output.add(printHop); } - else if(ptype == PRINTTYPE.ASSERT) { + else if (ptype == PRINTTYPE.ASSERT) { OpOp1 op = OpOp1.ASSERT; Expression source = ps.getExpressions().get(0); Hop ae = processExpression(source, target, ids); - Hop printHop = new UnaryOp(target.getName(), target.getDataType(), target.getValueType(), op, - ae); + Hop printHop = new UnaryOp(target.getName(), target.getDataType(), target.getValueType(), op, ae); printHop.setParseInfo(current); output.add(printHop); } - else if(ptype == PRINTTYPE.STOP) { + else if (ptype == PRINTTYPE.STOP) { OpOp1 op = OpOp1.STOP; Expression source = ps.getExpressions().get(0); Hop ae = processExpression(source, target, ids); - Hop stopHop = new UnaryOp(target.getName(), target.getDataType(), target.getValueType(), op, - ae); + Hop stopHop = new UnaryOp(target.getName(), target.getDataType(), target.getValueType(), op, ae); stopHop.setParseInfo(current); output.add(stopHop); - sb.setSplitDag(true); // avoid merge - } - else if(ptype == PRINTTYPE.PRINTF) { + sb.setSplitDag(true); //avoid merge + } else if (ptype == PRINTTYPE.PRINTF) { List expressions = ps.getExpressions(); Hop[] inHops = new Hop[expressions.size()]; // process the expressions (function parameters) that // make up the printf-styled print statement // into Hops so that these can be passed to the printf // Hop (ie, MultipleOp) as input Hops - for(int j = 0; j < expressions.size(); j++) { + for (int j = 0; j < expressions.size(); j++) { Hop inHop = processExpression(expressions.get(j), target, ids); inHops[j] = inHop; } target.setValueType(ValueType.STRING); - Hop printfHop = new NaryOp(target.getName(), target.getDataType(), target.getValueType(), - OpOpN.PRINTF, inHops); + Hop printfHop = new NaryOp(target.getName(), target.getDataType(), + target.getValueType(), OpOpN.PRINTF, inHops); output.add(printfHop); } - } - catch(HopsException e) { + } catch (HopsException e) { throw new LanguageException(e); } } - if(current instanceof AssignmentStatement) { + if (current instanceof AssignmentStatement) { AssignmentStatement as = (AssignmentStatement) current; DataIdentifier target = as.getTarget(); Expression source = as.getSource(); - // CASE: regular assignment statement -- source is DML expression that is NOT user-defined or external - // function - if(!(source instanceof FunctionCallIdentifier)) { + // CASE: regular assignment statement -- source is DML expression that is NOT user-defined or external function + if (!(source instanceof FunctionCallIdentifier)){ // CASE: target is regular data identifier - if(!(target instanceof IndexedIdentifier)) { - // process right hand side and accumulation + if (!(target instanceof IndexedIdentifier)) { + //process right hand side and accumulation Hop ae = processExpression(source, target, ids); - if(as.isAccumulator()) { + if( as.isAccumulator() ) { DataIdentifier accum = getAccumulatorData(liveIn, target.getName()); ae = HopRewriteUtils.createBinary(ids.get(target.getName()), ae, OpOp2.PLUS); target.setProperties(accum.getOutput()); @@ -1150,33 +1153,31 @@ else if(ptype == PRINTTYPE.PRINTF) { else target.setProperties(source.getOutput()); - if(source instanceof BuiltinFunctionExpression) { - BuiltinFunctionExpression BuiltinSource = (BuiltinFunctionExpression) source; - if(BuiltinSource.getOpCode() == Builtins.TIME) + if (source instanceof BuiltinFunctionExpression){ + BuiltinFunctionExpression BuiltinSource = (BuiltinFunctionExpression)source; + if (BuiltinSource.getOpCode() == Builtins.TIME) sb.setSplitDag(true); } ids.put(target.getName(), ae); - // add transient write if needed + //add transient write if needed Integer statementId = liveOutToTemp.get(target.getName()); - if((statementId != null) && (statementId.intValue() == i)) { - DataOp transientwrite = new DataOp(target.getName(), target.getDataType(), - target.getValueType(), ae, OpOpData.TRANSIENTWRITE, null); - transientwrite.setOutputParams(ae.getDim1(), ae.getDim2(), ae.getNnz(), ae.getUpdateType(), - ae.getBlocksize()); + if ((statementId != null) && (statementId.intValue() == i)) { + DataOp transientwrite = new DataOp(target.getName(), target.getDataType(), target.getValueType(), ae, OpOpData.TRANSIENTWRITE, null); + transientwrite.setOutputParams(ae.getDim1(), ae.getDim2(), ae.getNnz(), ae.getUpdateType(), ae.getBlocksize()); transientwrite.setParseInfo(target); updatedLiveOut.addVariable(target.getName(), target); output.add(transientwrite); } - } + } // CASE: target is indexed identifier (left-hand side indexed expression) else { - Hop ae = processLeftIndexedExpression(source, (IndexedIdentifier) target, ids); + Hop ae = processLeftIndexedExpression(source, (IndexedIdentifier)target, ids); - if(as.isAccumulator()) { + if( as.isAccumulator() ) { DataIdentifier accum = getAccumulatorData(liveIn, target.getName()); - Hop rix = processIndexingExpression((IndexedIdentifier) target, null, ids); + Hop rix = processIndexingExpression((IndexedIdentifier)target, null, ids); Hop rhs = processExpression(source, null, ids); Hop binary = HopRewriteUtils.createBinary(rix, rhs, OpOp2.PLUS); HopRewriteUtils.replaceChildReference(ae, ae.getInput(1), binary); @@ -1186,133 +1187,126 @@ else if(ptype == PRINTTYPE.PRINTF) { ids.put(target.getName(), ae); // obtain origDim values BEFORE they are potentially updated during setProperties call - // (this is incorrect for LHS Indexing) - long origDim1 = ((IndexedIdentifier) target).getOrigDim1(); - long origDim2 = ((IndexedIdentifier) target).getOrigDim2(); + // (this is incorrect for LHS Indexing) + long origDim1 = ((IndexedIdentifier)target).getOrigDim1(); + long origDim2 = ((IndexedIdentifier)target).getOrigDim2(); target.setProperties(source.getOutput()); - ((IndexedIdentifier) target).setOriginalDimensions(origDim1, origDim2); + ((IndexedIdentifier)target).setOriginalDimensions(origDim1, origDim2); // preserve data type matrix of any index identifier // (required for scalar input to left indexing) - if(target.getDataType() != DataType.MATRIX) { + if( target.getDataType() != DataType.MATRIX ) { target.setDataType(DataType.MATRIX); target.setValueType(ValueType.FP64); target.setBlocksize(ConfigurationManager.getBlocksize()); } Integer statementId = liveOutToTemp.get(target.getName()); - if((statementId != null) && (statementId.intValue() == i)) { - DataOp transientwrite = new DataOp(target.getName(), target.getDataType(), - target.getValueType(), ae, OpOpData.TRANSIENTWRITE, null); - transientwrite.setOutputParams(origDim1, origDim2, ae.getNnz(), ae.getUpdateType(), - ae.getBlocksize()); + if ((statementId != null) && (statementId.intValue() == i)) { + DataOp transientwrite = new DataOp(target.getName(), target.getDataType(), target.getValueType(), ae, OpOpData.TRANSIENTWRITE, null); + transientwrite.setOutputParams(origDim1, origDim2, ae.getNnz(), ae.getUpdateType(), ae.getBlocksize()); transientwrite.setParseInfo(target); updatedLiveOut.addVariable(target.getName(), target); output.add(transientwrite); } } } - else { - // assignment, function call + else + { + //assignment, function call FunctionCallIdentifier fci = (FunctionCallIdentifier) source; - FunctionStatementBlock fsb = this._dmlProg.getFunctionStatementBlock(fci.getNamespace(), - fci.getName()); + FunctionStatementBlock fsb = this._dmlProg.getFunctionStatementBlock(fci.getNamespace(),fci.getName()); - // error handling missing function - if(fsb == null) { - throw new LanguageException(source.printErrorLocation() + "function " + fci.getName() - + " is undefined in namespace " + fci.getNamespace()); + //error handling missing function + if (fsb == null) { + throw new LanguageException(source.printErrorLocation() + "function " + + fci.getName() + " is undefined in namespace " + fci.getNamespace()); } - FunctionStatement fstmt = (FunctionStatement) fsb.getStatement(0); - String fkey = DMLProgram.constructFunctionKey(fci.getNamespace(), fci.getName()); + FunctionStatement fstmt = (FunctionStatement)fsb.getStatement(0); + String fkey = DMLProgram.constructFunctionKey(fci.getNamespace(),fci.getName()); - // error handling unsupported function call in indexing expression - if(target instanceof IndexedIdentifier) { - throw new LanguageException("Unsupported function call to '" + fkey + "' in left indexing " + //error handling unsupported function call in indexing expression + if( target instanceof IndexedIdentifier ) { + throw new LanguageException("Unsupported function call to '"+fkey+"' in left indexing " + "expression. Please, assign the function output to a variable."); } - // prepare function input names and inputs - List inputNames = new ArrayList<>( - fci.getParamExprs().stream().map(e -> e.getName()).collect(Collectors.toList())); + //prepare function input names and inputs + List inputNames = new ArrayList<>(fci.getParamExprs().stream() + .map(e -> e.getName()).collect(Collectors.toList())); List finputs = new ArrayList<>(fci.getParamExprs().stream() .map(e -> processExpression(e.getExpr(), null, ids)).collect(Collectors.toList())); - // append default expression for missing arguments + //append default expression for missing arguments appendDefaultArguments(fstmt, inputNames, finputs, ids); - // use function signature to obtain names for unnamed args - // (note: consistent parameters already checked for functions in general) - if(inputNames.stream().allMatch(n -> n == null)) + //use function signature to obtain names for unnamed args + //(note: consistent parameters already checked for functions in general) + if( inputNames.stream().allMatch(n -> n==null) ) inputNames = fstmt._inputParams.stream().map(d -> d.getName()).collect(Collectors.toList()); - // create function op + //create function op String[] inputNames2 = inputNames.toArray(new String[0]); FunctionType ftype = fsb.getFunctionOpType(); - FunctionOp fcall = (target == null) ? new FunctionOp(ftype, fci.getNamespace(), fci.getName(), - inputNames2, finputs, new String[] {}, false) : new FunctionOp(ftype, fci.getNamespace(), - fci.getName(), inputNames2, finputs, new String[] {target.getName()}, false); + FunctionOp fcall = (target == null) ? + new FunctionOp(ftype, fci.getNamespace(), fci.getName(), inputNames2, finputs, new String[]{}, false) : + new FunctionOp(ftype, fci.getNamespace(), fci.getName(), inputNames2, finputs, new String[]{target.getName()}, false); fcall.setParseInfo(fci); output.add(fcall); } } - else if(current instanceof MultiAssignmentStatement) { - // multi-assignment, by definition a function call + else if (current instanceof MultiAssignmentStatement) { + //multi-assignment, by definition a function call MultiAssignmentStatement mas = (MultiAssignmentStatement) current; Expression source = mas.getSource(); - if(source instanceof FunctionCallIdentifier) { + if ( source instanceof FunctionCallIdentifier ) { FunctionCallIdentifier fci = (FunctionCallIdentifier) source; - FunctionStatementBlock fsb = this._dmlProg.getFunctionStatementBlock(fci.getNamespace(), - fci.getName()); - if(fsb == null) { - throw new LanguageException(source.printErrorLocation() + "function " + fci.getName() - + " is undefined in namespace " + fci.getNamespace()); + FunctionStatementBlock fsb = this._dmlProg.getFunctionStatementBlock(fci.getNamespace(),fci.getName()); + if (fsb == null){ + throw new LanguageException(source.printErrorLocation() + "function " + + fci.getName() + " is undefined in namespace " + fci.getNamespace()); } - FunctionStatement fstmt = (FunctionStatement) fsb.getStatement(0); + FunctionStatement fstmt = (FunctionStatement)fsb.getStatement(0); - // prepare function input names and inputs - List inputNames = new ArrayList<>( - fci.getParamExprs().stream().map(e -> e.getName()).collect(Collectors.toList())); + //prepare function input names and inputs + List inputNames = new ArrayList<>(fci.getParamExprs().stream() + .map(e -> e.getName()).collect(Collectors.toList())); List finputs = new ArrayList<>(fci.getParamExprs().stream() .map(e -> processExpression(e.getExpr(), null, ids)).collect(Collectors.toList())); - // use function signature to obtain names for unnamed args - // (note: consistent parameters already checked for functions in general) - if(inputNames.stream().allMatch(n -> n == null)) + //use function signature to obtain names for unnamed args + //(note: consistent parameters already checked for functions in general) + if( inputNames.stream().allMatch(n -> n==null) ) inputNames = fstmt._inputParams.stream().map(d -> d.getName()).collect(Collectors.toList()); - // append default expression for missing arguments + //append default expression for missing arguments appendDefaultArguments(fstmt, inputNames, finputs, ids); - // create function op - String[] foutputs = mas.getTargetList().stream().map(d -> d.getName()).toArray(String[]::new); + //create function op + String[] foutputs = mas.getTargetList().stream() + .map(d -> d.getName()).toArray(String[]::new); FunctionType ftype = fsb.getFunctionOpType(); FunctionOp fcall = new FunctionOp(ftype, fci.getNamespace(), fci.getName(), inputNames.toArray(new String[0]), finputs, foutputs, false); fcall.setParseInfo(fci); output.add(fcall); } - else if(source instanceof BuiltinFunctionExpression && - ((BuiltinFunctionExpression) source).multipleReturns()) { + else if ( source instanceof BuiltinFunctionExpression && ((BuiltinFunctionExpression)source).multipleReturns() ) { // construct input hops - Hop fcall = processMultipleReturnBuiltinFunctionExpression((BuiltinFunctionExpression) source, - mas.getTargetList(), ids); + Hop fcall = processMultipleReturnBuiltinFunctionExpression((BuiltinFunctionExpression)source, mas.getTargetList(), ids); output.add(fcall); } - else if(source instanceof ParameterizedBuiltinFunctionExpression && - ((ParameterizedBuiltinFunctionExpression) source).multipleReturns()) { + else if ( source instanceof ParameterizedBuiltinFunctionExpression && ((ParameterizedBuiltinFunctionExpression)source).multipleReturns() ) { // construct input hops - Hop fcall = processMultipleReturnParameterizedBuiltinFunctionExpression( - (ParameterizedBuiltinFunctionExpression) source, mas.getTargetList(), ids); + Hop fcall = processMultipleReturnParameterizedBuiltinFunctionExpression((ParameterizedBuiltinFunctionExpression)source, mas.getTargetList(), ids); output.add(fcall); } else - throw new LanguageException( - "Class \"" + source.getClass() + "\" is not supported in Multiple Assignment statements"); + throw new LanguageException("Class \"" + source.getClass() + "\" is not supported in Multiple Assignment statements"); } } @@ -1322,31 +1316,29 @@ else if(source instanceof ParameterizedBuiltinFunctionExpression && private static DataIdentifier getAccumulatorData(VariableSet liveIn, String varname) { DataIdentifier accum = liveIn.getVariable(varname); - if(accum == null) - throw new LanguageException( - "Invalid accumulator assignment " + "to non-existing variable " + varname + "."); + if( accum == null ) + throw new LanguageException("Invalid accumulator assignment " + + "to non-existing variable "+varname+"."); return accum; } - private void appendDefaultArguments(FunctionStatement fstmt, List inputNames, List inputs, - HashMap ids) { - // NOTE: For default expressions of unspecified function arguments, we have two choices: - // either (a) compile ifelse(exist(argName),default, argName) into the function, or - // simply (b) add the default to the argument list of function calls when needed. - // We decided for (b) because it simplifies IPA and dynamic recompilation. + private void appendDefaultArguments(FunctionStatement fstmt, List inputNames, List inputs, HashMap ids) { + //NOTE: For default expressions of unspecified function arguments, we have two choices: + //either (a) compile ifelse(exist(argName),default, argName) into the function, or + //simply (b) add the default to the argument list of function calls when needed. + //We decided for (b) because it simplifies IPA and dynamic recompilation. - if(fstmt.getInputParams().size() == inputs.size()) + if( fstmt.getInputParams().size() == inputs.size() ) return; HashSet probeNames = new HashSet<>(inputNames); - for(DataIdentifier di : fstmt.getInputParams()) { - if(probeNames.contains(di.getName())) - continue; + for( DataIdentifier di : fstmt.getInputParams() ) { + if( probeNames.contains(di.getName()) ) continue; Expression exp = fstmt.getInputDefault(di.getName()); - if(exp == null) { - throw new LanguageException("Missing default expression for unspecified " + "function argument '" - + di.getName() + "' in call to function '" + fstmt.getName() + "'."); + if( exp == null ) { + throw new LanguageException("Missing default expression for unspecified " + + "function argument '"+di.getName()+"' in call to function '"+fstmt.getName()+"'."); } - // compile and add default expression + //compile and add default expression inputNames.add(di.getName()); inputs.add(processExpression(exp, null, ids)); } @@ -1361,12 +1353,12 @@ public void constructHopsForIfControlBlock(IfStatementBlock sb) { constructHopsForConditionalPredicate(sb); // handle if statement body - for(StatementBlock current : ifBody) { + for( StatementBlock current : ifBody ) { constructHops(current); } // handle else stmt body - for(StatementBlock current : elseBody) { + for( StatementBlock current : elseBody ) { constructHops(current); } } @@ -1376,24 +1368,24 @@ public void constructHopsForIfControlBlock(IfStatementBlock sb) { * * @param sb for statement block */ - public void constructHopsForForControlBlock(ForStatementBlock sb) { + public void constructHopsForForControlBlock(ForStatementBlock sb) { ForStatement fs = (ForStatement) sb.getStatement(0); ArrayList body = fs.getBody(); constructHopsForIterablePredicate(sb); - for(StatementBlock current : body) + for( StatementBlock current : body ) constructHops(current); } public void constructHopsForFunctionControlBlock(FunctionStatementBlock fsb) { - ArrayList body = ((FunctionStatement) fsb.getStatement(0)).getBody(); - for(StatementBlock current : body) + ArrayList body = ((FunctionStatement)fsb.getStatement(0)).getBody(); + for( StatementBlock current : body ) constructHops(current); } public void constructHopsForWhileControlBlock(WhileStatementBlock sb) { - ArrayList body = ((WhileStatement) sb.getStatement(0)).getBody(); + ArrayList body = ((WhileStatement)sb.getStatement(0)).getBody(); constructHopsForConditionalPredicate(sb); - for(StatementBlock current : body) + for( StatementBlock current : body ) constructHops(current); } @@ -1404,12 +1396,12 @@ public void constructHopsForConditionalPredicate(StatementBlock passedSB) { // set conditional predicate ConditionalPredicate cp = null; - if(passedSB instanceof WhileStatementBlock) { - WhileStatement ws = (WhileStatement) ((WhileStatementBlock) passedSB).getStatement(0); + if (passedSB instanceof WhileStatementBlock){ + WhileStatement ws = (WhileStatement) ((WhileStatementBlock)passedSB).getStatement(0); cp = ws.getConditionalPredicate(); - } - else if(passedSB instanceof IfStatementBlock) { - IfStatement ws = (IfStatement) ((IfStatementBlock) passedSB).getStatement(0); + } + else if (passedSB instanceof IfStatementBlock) { + IfStatement ws = (IfStatement) ((IfStatementBlock)passedSB).getStatement(0); cp = ws.getConditionalPredicate(); } else { @@ -1418,24 +1410,21 @@ else if(passedSB instanceof IfStatementBlock) { VariableSet varsRead = cp.variablesRead(); - for(String varName : varsRead.getVariables().keySet()) { + for (String varName : varsRead.getVariables().keySet()) { // creating transient read for live in variables DataIdentifier var = passedSB.liveIn().getVariables().get(varName); DataOp read = null; - if(var == null) { + if (var == null) { throw new ParseException("variable " + varName + " not live variable for conditional predicate"); - } - else { - long actualDim1 = (var instanceof IndexedIdentifier) ? ((IndexedIdentifier) var).getOrigDim1() : var - .getDim1(); - long actualDim2 = (var instanceof IndexedIdentifier) ? ((IndexedIdentifier) var).getOrigDim2() : var - .getDim2(); + } else { + long actualDim1 = (var instanceof IndexedIdentifier) ? ((IndexedIdentifier)var).getOrigDim1() : var.getDim1(); + long actualDim2 = (var instanceof IndexedIdentifier) ? ((IndexedIdentifier)var).getOrigDim2() : var.getDim2(); - read = new DataOp(var.getName(), var.getDataType(), var.getValueType(), OpOpData.TRANSIENTREAD, null, - actualDim1, actualDim2, var.getNnz(), var.getBlocksize()); + read = new DataOp(var.getName(), var.getDataType(), var.getValueType(), OpOpData.TRANSIENTREAD, + null, actualDim1, actualDim2, var.getNnz(), var.getBlocksize()); read.setParseInfo(var); } _ids.put(varName, read); @@ -1448,199 +1437,193 @@ else if(passedSB instanceof IfStatementBlock) { Hop predicateHops = null; Expression predicate = cp.getPredicate(); - if(predicate instanceof RelationalExpression) { + if (predicate instanceof RelationalExpression) { predicateHops = processRelationalExpression((RelationalExpression) cp.getPredicate(), target, _ids); - } - else if(predicate instanceof BooleanExpression) { + } else if (predicate instanceof BooleanExpression) { predicateHops = processBooleanExpression((BooleanExpression) cp.getPredicate(), target, _ids); - } - else if(predicate instanceof DataIdentifier) { + } else if (predicate instanceof DataIdentifier) { // handle data identifier predicate predicateHops = processExpression(cp.getPredicate(), null, _ids); - } - else if(predicate instanceof ConstIdentifier) { + } else if (predicate instanceof ConstIdentifier) { // handle constant identifier - // a) translate 0 --> FALSE; translate 1 --> TRUE - // b) disallow string values - if((predicate instanceof IntIdentifier && ((IntIdentifier) predicate).getValue() == 0) || - (predicate instanceof DoubleIdentifier && ((DoubleIdentifier) predicate).getValue() == 0.0)) { + // a) translate 0 --> FALSE; translate 1 --> TRUE + // b) disallow string values + if ((predicate instanceof IntIdentifier && ((IntIdentifier) predicate).getValue() == 0) + || (predicate instanceof DoubleIdentifier && ((DoubleIdentifier) predicate).getValue() == 0.0)) { cp.setPredicate(new BooleanIdentifier(false, predicate)); - } - else if((predicate instanceof IntIdentifier && ((IntIdentifier) predicate).getValue() == 1) || - (predicate instanceof DoubleIdentifier && ((DoubleIdentifier) predicate).getValue() == 1.0)) { + } else if ((predicate instanceof IntIdentifier && ((IntIdentifier) predicate).getValue() == 1) + || (predicate instanceof DoubleIdentifier && ((DoubleIdentifier) predicate).getValue() == 1.0)) { cp.setPredicate(new BooleanIdentifier(true, predicate)); - } - else if(predicate instanceof IntIdentifier || predicate instanceof DoubleIdentifier) { + } else if (predicate instanceof IntIdentifier || predicate instanceof DoubleIdentifier) { cp.setPredicate(new BooleanIdentifier(true, predicate)); LOG.warn(predicate.printWarningLocation() + "Numerical value '" + predicate.toString() - + "' (!= 0/1) is converted to boolean TRUE by DML"); - } - else if(predicate instanceof StringIdentifier) { + + "' (!= 0/1) is converted to boolean TRUE by DML"); + } else if (predicate instanceof StringIdentifier) { throw new ParseException(predicate.printErrorLocation() + "String value '" + predicate.toString() - + "' is not allowed for iterable predicate"); + + "' is not allowed for iterable predicate"); } predicateHops = processExpression(cp.getPredicate(), null, _ids); } - // create transient write to internal variable name on top of expression - // in order to ensure proper instruction generation - predicateHops = HopRewriteUtils.createDataOp(ProgramBlock.PRED_VAR, predicateHops, OpOpData.TRANSIENTWRITE); + //create transient write to internal variable name on top of expression + //in order to ensure proper instruction generation + predicateHops = HopRewriteUtils.createDataOp( + ProgramBlock.PRED_VAR, predicateHops, OpOpData.TRANSIENTWRITE); - if(passedSB instanceof WhileStatementBlock) - ((WhileStatementBlock) passedSB).setPredicateHops(predicateHops); - else if(passedSB instanceof IfStatementBlock) - ((IfStatementBlock) passedSB).setPredicateHops(predicateHops); + if (passedSB instanceof WhileStatementBlock) + ((WhileStatementBlock)passedSB).setPredicateHops(predicateHops); + else if (passedSB instanceof IfStatementBlock) + ((IfStatementBlock)passedSB).setPredicateHops(predicateHops); } /** - * Constructs all predicate Hops (for FROM, TO, INCREMENT) of an iterable predicate and assigns these Hops to the - * passed statement block. + * Constructs all predicate Hops (for FROM, TO, INCREMENT) of an iterable predicate + * and assigns these Hops to the passed statement block. * * Method used for both ForStatementBlock and ParForStatementBlock. * * @param fsb for statement block */ - public void constructHopsForIterablePredicate(ForStatementBlock fsb) { + public void constructHopsForIterablePredicate(ForStatementBlock fsb) + { HashMap _ids = new HashMap<>(); - // set iterable predicate + // set iterable predicate ForStatement fs = (ForStatement) fsb.getStatement(0); IterablePredicate ip = fs.getIterablePredicate(); - for(int i = 0; i < 3; i++) { - Expression expr = (i == 0) ? ip.getFromExpr() : (i == 1) ? ip - .getToExpr() : (ip.getIncrementExpr() != null) ? ip.getIncrementExpr() : null; + for(int i=0; i < 3; i++) { + Expression expr = (i == 0) ? ip.getFromExpr() : (i == 1) ? ip.getToExpr() : + ( ip.getIncrementExpr() != null ) ? ip.getIncrementExpr() : null; VariableSet varsRead = (expr != null) ? expr.variablesRead() : null; if(varsRead != null) { - for(String varName : varsRead.getVariables().keySet()) { + for (String varName : varsRead.getVariables().keySet()) { DataIdentifier var = fsb.liveIn().getVariable(varName); DataOp read = null; - if(var == null) { + if (var == null) { throw new ParseException("variable '" + varName + "' is not available for iterable predicate"); } else { - long actualDim1 = (var instanceof IndexedIdentifier) ? ((IndexedIdentifier) var) - .getOrigDim1() : var.getDim1(); - long actualDim2 = (var instanceof IndexedIdentifier) ? ((IndexedIdentifier) var) - .getOrigDim2() : var.getDim2(); + long actualDim1 = (var instanceof IndexedIdentifier) ? ((IndexedIdentifier)var).getOrigDim1() : var.getDim1(); + long actualDim2 = (var instanceof IndexedIdentifier) ? ((IndexedIdentifier)var).getOrigDim2() : var.getDim2(); read = new DataOp(var.getName(), var.getDataType(), var.getValueType(), OpOpData.TRANSIENTREAD, - null, actualDim1, actualDim2, var.getNnz(), var.getBlocksize()); + null, actualDim1, actualDim2, var.getNnz(), var.getBlocksize()); read.setParseInfo(var); } _ids.put(varName, read); } } - // create transient write to internal variable name on top of expression - // in order to ensure proper instruction generation + //create transient write to internal variable name on top of expression + //in order to ensure proper instruction generation Hop predicateHops = processTempIntExpression(expr, _ids); - if(predicateHops != null) - predicateHops = HopRewriteUtils.createDataOp(ProgramBlock.PRED_VAR, predicateHops, - OpOpData.TRANSIENTWRITE); - - // construct hops for from, to, and increment expressions - if(i == 0) - fsb.setFromHops(predicateHops); - else if(i == 1) - fsb.setToHops(predicateHops); - else if(ip.getIncrementExpr() != null) - fsb.setIncrementHops(predicateHops); + if( predicateHops != null ) + predicateHops = HopRewriteUtils.createDataOp( + ProgramBlock.PRED_VAR, predicateHops, OpOpData.TRANSIENTWRITE); + + //construct hops for from, to, and increment expressions + if( i == 0 ) + fsb.setFromHops( predicateHops ); + else if( i == 1 ) + fsb.setToHops( predicateHops ); + else if( ip.getIncrementExpr() != null ) + fsb.setIncrementHops( predicateHops ); } } /** - * Construct Hops from parse tree : Process Expression in an assignment statement + * Construct Hops from parse tree : Process Expression in an assignment + * statement * * @param source source expression * @param target data identifier - * @param hops map of high-level operators + * @param hops map of high-level operators * @return high-level operator */ private Hop processExpression(Expression source, DataIdentifier target, HashMap hops) { try { - if(source instanceof BinaryExpression) + if( source instanceof BinaryExpression ) return processBinaryExpression((BinaryExpression) source, target, hops); - else if(source instanceof RelationalExpression) + else if( source instanceof RelationalExpression ) return processRelationalExpression((RelationalExpression) source, target, hops); - else if(source instanceof BooleanExpression) + else if( source instanceof BooleanExpression ) return processBooleanExpression((BooleanExpression) source, target, hops); - else if(source instanceof BuiltinFunctionExpression) + else if( source instanceof BuiltinFunctionExpression ) return processBuiltinFunctionExpression((BuiltinFunctionExpression) source, target, hops); - else if(source instanceof ParameterizedBuiltinFunctionExpression) - return processParameterizedBuiltinFunctionExpression((ParameterizedBuiltinFunctionExpression) source, - target, hops); - else if(source instanceof DataExpression) { - Hop ae = processDataExpression((DataExpression) source, target, hops); - if(ae instanceof DataOp && ((DataOp) ae).getOp() != OpOpData.SQLREAD && - ((DataOp) ae).getOp() != OpOpData.FEDERATED) { - Expression expr = ((DataExpression) source).getVarParam(DataExpression.FORMAT_TYPE); - if(expr instanceof StringIdentifier) - ((DataOp) ae).setFileFormat(Expression.convertFormatType(expr.toString())); + else if( source instanceof ParameterizedBuiltinFunctionExpression ) + return processParameterizedBuiltinFunctionExpression((ParameterizedBuiltinFunctionExpression)source, target, hops); + else if( source instanceof DataExpression ) { + Hop ae = processDataExpression((DataExpression)source, target, hops); + if (ae instanceof DataOp && ((DataOp) ae).getOp() != OpOpData.SQLREAD && + ((DataOp) ae).getOp() != OpOpData.FEDERATED) { + Expression expr = ((DataExpression)source).getVarParam(DataExpression.FORMAT_TYPE); + if( expr instanceof StringIdentifier ) + ((DataOp)ae).setFileFormat(Expression.convertFormatType(expr.toString())); else - ((DataOp) ae).setFileFormat(FileFormat.UNKNOWN); + ((DataOp)ae).setFileFormat(FileFormat.UNKNOWN); } return ae; } - else if(source instanceof IndexedIdentifier) - return processIndexingExpression((IndexedIdentifier) source, target, hops); - else if(source instanceof IntIdentifier) { + else if (source instanceof IndexedIdentifier) + return processIndexingExpression((IndexedIdentifier) source,target,hops); + else if (source instanceof IntIdentifier) { IntIdentifier sourceInt = (IntIdentifier) source; LiteralOp litop = new LiteralOp(sourceInt.getValue()); litop.setParseInfo(sourceInt); setIdentifierParams(litop, sourceInt); return litop; - } - else if(source instanceof DoubleIdentifier) { + } + else if (source instanceof DoubleIdentifier) { DoubleIdentifier sourceDouble = (DoubleIdentifier) source; LiteralOp litop = new LiteralOp(sourceDouble.getValue()); litop.setParseInfo(sourceDouble); setIdentifierParams(litop, sourceDouble); return litop; } - else if(source instanceof BooleanIdentifier) { + else if (source instanceof BooleanIdentifier) { BooleanIdentifier sourceBoolean = (BooleanIdentifier) source; LiteralOp litop = new LiteralOp(sourceBoolean.getValue()); litop.setParseInfo(sourceBoolean); setIdentifierParams(litop, sourceBoolean); return litop; - } - else if(source instanceof StringIdentifier) { + } + else if (source instanceof StringIdentifier) { StringIdentifier sourceString = (StringIdentifier) source; LiteralOp litop = new LiteralOp(sourceString.getValue()); litop.setParseInfo(sourceString); setIdentifierParams(litop, sourceString); return litop; - } - else if(source instanceof DataIdentifier) + } + else if (source instanceof DataIdentifier) return hops.get(((DataIdentifier) source).getName()); - else if(source instanceof ExpressionList) { + else if (source instanceof ExpressionList){ ExpressionList sourceList = (ExpressionList) source; List expressions = sourceList.getValue(); Hop[] listHops = new Hop[expressions.size()]; int idx = 0; - for(Expression ex : expressions) { + for( Expression ex : expressions){ listHops[idx++] = processExpression(ex, null, hops); } - Hop currBuiltinOp = HopRewriteUtils.createNary(OpOpN.LIST, listHops); + Hop currBuiltinOp = HopRewriteUtils.createNary(OpOpN.LIST,listHops ); return currBuiltinOp; } - else { + else{ throw new ParseException("Unhandled instance of source type: " + source.getClass()); } - } - catch(ParseException e) { + } + catch(ParseException e ){ throw e; } - catch(Exception e) { + catch ( Exception e ) { throw new ParseException("A Parsing exception occurred", e); } } private static DataIdentifier createTarget(Expression source) { Identifier id = source.getOutput(); - if(id instanceof DataIdentifier && !(id instanceof DataExpression)) + if (id instanceof DataIdentifier && !(id instanceof DataExpression)) return (DataIdentifier) id; DataIdentifier target = new DataIdentifier(Expression.getTempName()); target.setProperties(id); @@ -1652,20 +1635,20 @@ private static DataIdentifier createTarget() { } /** - * Constructs the Hops for arbitrary expressions that eventually evaluate to an INT scalar. + * Constructs the Hops for arbitrary expressions that eventually evaluate to an INT scalar. * * @param source source expression - * @param hops map of high-level operators + * @param hops map of high-level operators * @return high-level operatos */ - private Hop processTempIntExpression(Expression source, HashMap hops) { - if(source == null) + private Hop processTempIntExpression( Expression source, HashMap hops ) { + if( source == null ) return null; DataIdentifier tmpOut = createTarget(); tmpOut.setDataType(DataType.SCALAR); tmpOut.setValueType(ValueType.INT64); source.setOutput(tmpOut); - return processExpression(source, tmpOut, hops); + return processExpression(source, tmpOut, hops ); } private Hop processLeftIndexedExpression(Expression source, IndexedIdentifier target, HashMap hops) { @@ -1677,17 +1660,16 @@ private Hop processLeftIndexedExpression(Expression source, IndexedIdentifier ta // process the target to get targetHops Hop targetOp = hops.get(target.getName()); - if(targetOp == null) { - throw new ParseException(target.printErrorLocation() + " must define matrix " + target.getName() - + " before indexing operations are allowed "); + if (targetOp == null){ + throw new ParseException(target.printErrorLocation() + " must define matrix " + target.getName() + " before indexing operations are allowed "); } - if(sourceOp.getDataType().isMatrix() && source.getOutput().getDataType().isScalar()) + if( sourceOp.getDataType().isMatrix() && source.getOutput().getDataType().isScalar() ) sourceOp.setDataType(DataType.SCALAR); - Hop leftIndexOp = new LeftIndexingOp(target.getName(), target.getDataType(), ValueType.FP64, targetOp, sourceOp, - ixRange[0], ixRange[1], ixRange[2], ixRange[3], target.getRowLowerEqualsUpper(), - target.getColLowerEqualsUpper()); + Hop leftIndexOp = new LeftIndexingOp(target.getName(), target.getDataType(), + ValueType.FP64, targetOp, sourceOp, ixRange[0], ixRange[1], ixRange[2], ixRange[3], + target.getRowLowerEqualsUpper(), target.getColLowerEqualsUpper()); setIdentifierParams(leftIndexOp, target); leftIndexOp.setParseInfo(target); @@ -1701,16 +1683,16 @@ private Hop processIndexingExpression(IndexedIdentifier source, DataIdentifier t // process Hops for indexes (for source) Hop[] ixRange = getIndexingBounds(source, hops, false); - if(target == null) { + if (target == null) { target = createTarget(source); } - // unknown nnz after range indexing (applies to indexing op but also - // data dependent operations) - target.setNnz(-1); + //unknown nnz after range indexing (applies to indexing op but also + //data dependent operations) + target.setNnz(-1); Hop indexOp = new IndexingOp(target.getName(), target.getDataType(), target.getValueType(), - hops.get(source.getName()), ixRange[0], ixRange[1], ixRange[2], ixRange[3], source.getRowLowerEqualsUpper(), - source.getColLowerEqualsUpper()); + hops.get(source.getName()), ixRange[0], ixRange[1], ixRange[2], ixRange[3], + source.getRowLowerEqualsUpper(), source.getColLowerEqualsUpper()); indexOp.setParseInfo(target); setIdentifierParams(indexOp, target); @@ -1719,27 +1701,27 @@ private Hop processIndexingExpression(IndexedIdentifier source, DataIdentifier t } private Hop[] getIndexingBounds(IndexedIdentifier ix, HashMap hops, boolean lix) { - Hop rowLowerHops = (ix.getRowLowerBound() != null) ? processExpression(ix.getRowLowerBound(), null, - hops) : new LiteralOp(1); - Hop colLowerHops = (ix.getColLowerBound() != null) ? processExpression(ix.getColLowerBound(), null, - hops) : new LiteralOp(1); + Hop rowLowerHops = (ix.getRowLowerBound() != null) ? + processExpression(ix.getRowLowerBound(),null, hops) : new LiteralOp(1); + Hop colLowerHops = (ix.getColLowerBound() != null) ? + processExpression(ix.getColLowerBound(),null, hops) : new LiteralOp(1); Hop rowUpperHops = null, colUpperHops = null; - if(ix.getRowUpperBound() != null) - rowUpperHops = processExpression(ix.getRowUpperBound(), null, hops); + if (ix.getRowUpperBound() != null) + rowUpperHops = processExpression(ix.getRowUpperBound(),null,hops); else { - rowUpperHops = ((lix ? ix.getDim1() : ix.getOrigDim1()) != -1) ? new LiteralOp( - ix.getOrigDim1()) : new UnaryOp(ix.getName(), DataType.SCALAR, ValueType.INT64, OpOp1.NROW, - hops.get(ix.getName())); + rowUpperHops = ((lix ? ix.getDim1() : ix.getOrigDim1()) != -1) ? + new LiteralOp(ix.getOrigDim1()) : + new UnaryOp(ix.getName(), DataType.SCALAR, ValueType.INT64, OpOp1.NROW, hops.get(ix.getName())); rowUpperHops.setParseInfo(ix); } - if(ix.getColUpperBound() != null) - colUpperHops = processExpression(ix.getColUpperBound(), null, hops); + if (ix.getColUpperBound() != null) + colUpperHops = processExpression(ix.getColUpperBound(),null,hops); else { - colUpperHops = ((lix ? ix.getDim2() : ix.getOrigDim2()) != -1) ? new LiteralOp( - ix.getOrigDim2()) : new UnaryOp(ix.getName(), DataType.SCALAR, ValueType.INT64, OpOp1.NCOL, - hops.get(ix.getName())); + colUpperHops = ((lix ? ix.getDim2() : ix.getOrigDim2()) != -1) ? + new LiteralOp(ix.getOrigDim2()) : + new UnaryOp(ix.getName(), DataType.SCALAR, ValueType.INT64, OpOp1.NCOL, hops.get(ix.getName())); colUpperHops.setParseInfo(ix); } @@ -1747,31 +1729,33 @@ private Hop[] getIndexingBounds(IndexedIdentifier ix, HashMap hops, } /** - * Construct Hops from parse tree : Process Binary Expression in an assignment statement + * Construct Hops from parse tree : Process Binary Expression in an + * assignment statement * * @param source binary expression * @param target data identifier - * @param hops map of high-level operators + * @param hops map of high-level operators * @return high-level operator */ - private Hop processBinaryExpression(BinaryExpression source, DataIdentifier target, HashMap hops) { - Hop left = processExpression(source.getLeft(), null, hops); + private Hop processBinaryExpression(BinaryExpression source, DataIdentifier target, HashMap hops) + { + Hop left = processExpression(source.getLeft(), null, hops); Hop right = processExpression(source.getRight(), null, hops); - if(left == null || right == null) { - throw new ParseException("Missing input in binary expressions (" + source.toString() + "): " - + ((left == null) ? source.getLeft() : source.getRight()) + ", line=" + source.getBeginLine()); + if (left == null || right == null) { + throw new ParseException("Missing input in binary expressions (" + source.toString()+"): " + + ((left==null)?source.getLeft():source.getRight())+", line="+source.getBeginLine()); } - // prepare target identifier and ensure that output type is of inferred type - // (type should not be determined by target (e.g., string for print) - if(target == null) { + //prepare target identifier and ensure that output type is of inferred type + //(type should not be determined by target (e.g., string for print) + if (target == null) { target = createTarget(source); } target.setValueType(source.getOutput().getValueType()); Hop currBop = null; - switch(source.getOpCode()) { + switch( source.getOpCode() ) { case PLUS: case MINUS: case MULT: @@ -1779,15 +1763,14 @@ private Hop processBinaryExpression(BinaryExpression source, DataIdentifier targ case MODULUS: case POW: case INTDIV: - currBop = new BinaryOp(target.getName(), target.getDataType(), target.getValueType(), - OpOp2.valueOf(source.getOpCode().name()), left, right); + currBop = new BinaryOp(target.getName(), target.getDataType(), + target.getValueType(), OpOp2.valueOf(source.getOpCode().name()), left, right); break; case MATMULT: - currBop = new AggBinaryOp(target.getName(), target.getDataType(), target.getValueType(), OpOp2.MULT, - org.apache.sysds.common.Types.AggOp.SUM, left, right); + currBop = new AggBinaryOp(target.getName(), target.getDataType(), target.getValueType(), OpOp2.MULT, org.apache.sysds.common.Types.AggOp.SUM, left, right); break; default: - throw new ParseException("Unsupported parsing of binary expression: " + source.getOpCode()); + throw new ParseException("Unsupported parsing of binary expression: "+source.getOpCode()); } setIdentifierParams(currBop, source.getOutput()); @@ -1795,15 +1778,14 @@ private Hop processBinaryExpression(BinaryExpression source, DataIdentifier targ return currBop; } - private Hop processRelationalExpression(RelationalExpression source, DataIdentifier target, - HashMap hops) { + private Hop processRelationalExpression(RelationalExpression source, DataIdentifier target, HashMap hops) { Hop left = processExpression(source.getLeft(), null, hops); Hop right = processExpression(source.getRight(), null, hops); Hop currBop = null; - if(target == null) { + if (target == null) { target = createTarget(source); if(left.getDataType() == DataType.MATRIX || right.getDataType() == DataType.MATRIX) { // Added to support matrix relational comparison @@ -1824,22 +1806,17 @@ else if(left.getDataType() == DataType.FRAME || right.getDataType() == DataType. OpOp2 op = null; - if(source.getOpCode() == Expression.RelationalOp.LESS) { + if (source.getOpCode() == Expression.RelationalOp.LESS) { op = OpOp2.LESS; - } - else if(source.getOpCode() == Expression.RelationalOp.LESSEQUAL) { + } else if (source.getOpCode() == Expression.RelationalOp.LESSEQUAL) { op = OpOp2.LESSEQUAL; - } - else if(source.getOpCode() == Expression.RelationalOp.GREATER) { + } else if (source.getOpCode() == Expression.RelationalOp.GREATER) { op = OpOp2.GREATER; - } - else if(source.getOpCode() == Expression.RelationalOp.GREATEREQUAL) { + } else if (source.getOpCode() == Expression.RelationalOp.GREATEREQUAL) { op = OpOp2.GREATEREQUAL; - } - else if(source.getOpCode() == Expression.RelationalOp.EQUAL) { + } else if (source.getOpCode() == Expression.RelationalOp.EQUAL) { op = OpOp2.EQUAL; - } - else if(source.getOpCode() == Expression.RelationalOp.NOTEQUAL) { + } else if (source.getOpCode() == Expression.RelationalOp.NOTEQUAL) { op = OpOp2.NOTEQUAL; } currBop = new BinaryOp(target.getName(), target.getDataType(), target.getValueType(), op, left, right); @@ -1847,49 +1824,47 @@ else if(source.getOpCode() == Expression.RelationalOp.NOTEQUAL) { return currBop; } - private Hop processBooleanExpression(BooleanExpression source, DataIdentifier target, HashMap hops) { + private Hop processBooleanExpression(BooleanExpression source, DataIdentifier target, HashMap hops) + { // Boolean Not has a single parameter boolean constLeft = (source.getLeft().getOutput() instanceof ConstIdentifier); boolean constRight = false; - if(source.getRight() != null) { + if (source.getRight() != null) { constRight = (source.getRight().getOutput() instanceof ConstIdentifier); } - if(constLeft || constRight) { + if (constLeft || constRight) { throw new RuntimeException(source.printErrorLocation() + "Boolean expression with constant unsupported"); } Hop left = processExpression(source.getLeft(), null, hops); Hop right = null; - if(source.getRight() != null) { + if (source.getRight() != null) { right = processExpression(source.getRight(), null, hops); } - // prepare target identifier and ensure that output type is boolean - // (type should not be determined by target (e.g., string for print) - if(target == null) + //prepare target identifier and ensure that output type is boolean + //(type should not be determined by target (e.g., string for print) + if (target == null) target = createTarget(source); - if(target.getDataType().isScalar()) + if( target.getDataType().isScalar() ) target.setValueType(ValueType.BOOLEAN); - if(source.getRight() == null) { + if (source.getRight() == null) { Hop currUop = new UnaryOp(target.getName(), target.getDataType(), target.getValueType(), OpOp1.NOT, left); currUop.setParseInfo(source); return currUop; - } + } else { Hop currBop = null; OpOp2 op = null; - if(source.getOpCode() == Expression.BooleanOp.LOGICALAND) { + if (source.getOpCode() == Expression.BooleanOp.LOGICALAND) { op = OpOp2.AND; - } - else if(source.getOpCode() == Expression.BooleanOp.LOGICALOR) { + } else if (source.getOpCode() == Expression.BooleanOp.LOGICALOR) { op = OpOp2.OR; - } - else { - throw new RuntimeException( - source.printErrorLocation() + "Unknown boolean operation " + source.getOpCode()); + } else { + throw new RuntimeException(source.printErrorLocation() + "Unknown boolean operation " + source.getOpCode()); } currBop = new BinaryOp(target.getName(), target.getDataType(), target.getValueType(), op, left, right); currBop.setParseInfo(source); @@ -1898,51 +1873,49 @@ else if(source.getOpCode() == Expression.BooleanOp.LOGICALOR) { } } - private static Hop constructDfHop(String name, DataType dt, ValueType vt, Builtins op, - LinkedHashMap paramHops) { + private static Hop constructDfHop(String name, DataType dt, ValueType vt, Builtins op, LinkedHashMap paramHops) { - // Add a hop to paramHops to store distribution information. + // Add a hop to paramHops to store distribution information. // Distribution parameter hops would have been already present in paramHops. Hop distLop = null; switch(op) { - case QNORM: - case PNORM: - distLop = new LiteralOp("normal"); - break; - case QT: - case PT: - distLop = new LiteralOp("t"); - break; - case QF: - case PF: - distLop = new LiteralOp("f"); - break; - case QCHISQ: - case PCHISQ: - distLop = new LiteralOp("chisq"); - break; - case QEXP: - case PEXP: - distLop = new LiteralOp("exp"); - break; - - case CDF: - case INVCDF: - break; - - default: - throw new HopsException("Invalid operation: " + op); - } - if(distLop != null) + case QNORM: + case PNORM: + distLop = new LiteralOp("normal"); + break; + case QT: + case PT: + distLop = new LiteralOp("t"); + break; + case QF: + case PF: + distLop = new LiteralOp("f"); + break; + case QCHISQ: + case PCHISQ: + distLop = new LiteralOp("chisq"); + break; + case QEXP: + case PEXP: + distLop = new LiteralOp("exp"); + break; + + case CDF: + case INVCDF: + break; + + default: + throw new HopsException("Invalid operation: " + op); + } + if (distLop != null) paramHops.put("dist", distLop); - return new ParameterizedBuiltinOp(name, dt, vt, ParameterizedBuiltinFunctionExpression.pbHopMap.get(op), - paramHops); + return new ParameterizedBuiltinOp(name, dt, vt, ParameterizedBuiltinFunctionExpression.pbHopMap.get(op), paramHops); } - private Hop processMultipleReturnParameterizedBuiltinFunctionExpression( - ParameterizedBuiltinFunctionExpression source, ArrayList targetList, - HashMap hops) { + private Hop processMultipleReturnParameterizedBuiltinFunctionExpression(ParameterizedBuiltinFunctionExpression source, ArrayList targetList, + HashMap hops) + { FunctionType ftype = FunctionType.MULTIRETURN_BUILTIN; String nameSpace = DMLProgram.INTERNAL_NAMESPACE; @@ -1952,32 +1925,27 @@ private Hop processMultipleReturnParameterizedBuiltinFunctionExpression( // Construct Hop for current builtin function expression based on its type Hop currBuiltinOp = null; - switch(source.getOpCode()) { + switch (source.getOpCode()) { case TRANSFORMENCODE: ArrayList inputs = new ArrayList<>(); - inputs.add(processExpression(source.getVarParam("target"), null, hops)); - inputs.add(processExpression(source.getVarParam("spec"), null, hops)); - String[] outputNames = new String[targetList.size()]; + inputs.add( processExpression(source.getVarParam("target"), null, hops) ); + inputs.add( processExpression(source.getVarParam("spec"), null, hops) ); + String[] outputNames = new String[targetList.size()]; outputNames[0] = targetList.get(0).getName(); outputNames[1] = targetList.get(1).getName(); - outputs.add(new DataOp(outputNames[0], DataType.MATRIX, ValueType.FP64, inputs.get(0), - OpOpData.FUNCTIONOUTPUT, inputs.get(0).getFilename())); - outputs.add(new DataOp(outputNames[1], DataType.FRAME, ValueType.STRING, inputs.get(0), - OpOpData.FUNCTIONOUTPUT, inputs.get(0).getFilename())); + outputs.add(new DataOp(outputNames[0], DataType.MATRIX, ValueType.FP64, inputs.get(0), OpOpData.FUNCTIONOUTPUT, inputs.get(0).getFilename())); + outputs.add(new DataOp(outputNames[1], DataType.FRAME, ValueType.STRING, inputs.get(0), OpOpData.FUNCTIONOUTPUT, inputs.get(0).getFilename())); - currBuiltinOp = new FunctionOp(ftype, nameSpace, source.getOpCode().toString(), null, inputs, - outputNames, outputs); + currBuiltinOp = new FunctionOp(ftype, nameSpace, source.getOpCode().toString(), null, inputs, outputNames, outputs); break; default: - throw new ParseException( - "Invaid Opcode in DMLTranslator:processMultipleReturnParameterizedBuiltinFunctionExpression(): " - + source.getOpCode()); + throw new ParseException("Invaid Opcode in DMLTranslator:processMultipleReturnParameterizedBuiltinFunctionExpression(): " + source.getOpCode()); } // set properties for created hops based on outputs of source expression - for(int i = 0; i < source.getOutputs().length; i++) { - setIdentifierParams(outputs.get(i), source.getOutputs()[i]); + for ( int i=0; i < source.getOutputs().length; i++ ) { + setIdentifierParams( outputs.get(i), source.getOutputs()[i]); outputs.get(i).setParseInfo(source); } currBuiltinOp.setParseInfo(source); @@ -1986,15 +1954,16 @@ private Hop processMultipleReturnParameterizedBuiltinFunctionExpression( } /** - * Construct Hops from parse tree : Process ParameterizedBuiltinFunction Expression in an assignment statement + * Construct Hops from parse tree : Process ParameterizedBuiltinFunction Expression in an + * assignment statement * * @param source parameterized built-in function * @param target data identifier - * @param hops map of high-level operators + * @param hops map of high-level operators * @return high-level operator */ - private Hop processParameterizedBuiltinFunctionExpression(ParameterizedBuiltinFunctionExpression source, - DataIdentifier target, HashMap hops) { + private Hop processParameterizedBuiltinFunctionExpression(ParameterizedBuiltinFunctionExpression source, DataIdentifier target, + HashMap hops) { // this expression has multiple "named" parameters LinkedHashMap paramHops = new LinkedHashMap<>(); @@ -2002,14 +1971,14 @@ private Hop processParameterizedBuiltinFunctionExpression(ParameterizedBuiltinFu // -- construct hops for all input parameters // -- store them in hashmap so that their "name"s are maintained Hop pHop = null; - for(String paramName : source.getVarParams().keySet()) { + for ( String paramName : source.getVarParams().keySet() ) { pHop = processExpression(source.getVarParam(paramName), null, hops); paramHops.put(paramName, pHop); } Hop currBuiltinOp = null; - if(target == null) { + if (target == null) { target = createTarget(source); } @@ -2027,8 +1996,8 @@ private Hop processParameterizedBuiltinFunctionExpression(ParameterizedBuiltinFu case PF: case PCHISQ: case PEXP: - currBuiltinOp = constructDfHop(target.getName(), target.getDataType(), target.getValueType(), - source.getOpCode(), paramHops); + currBuiltinOp = constructDfHop(target.getName(), target.getDataType(), + target.getValueType(), source.getOpCode(), paramHops); break; case CONTAINS: case GROUPEDAGG: @@ -2053,16 +2022,16 @@ private Hop processParameterizedBuiltinFunctionExpression(ParameterizedBuiltinFu inputs.add(paramHops.get("by")); inputs.add(paramHops.get("decreasing")); inputs.add(paramHops.get("index.return")); - currBuiltinOp = new ReorgOp(target.getName(), target.getDataType(), target.getValueType(), ReOrgOp.SORT, - inputs); + currBuiltinOp = new ReorgOp(target.getName(), target.getDataType(), target.getValueType(), ReOrgOp.SORT, inputs); break; case TOSTRING: - // check for input data type and only compile toString Hop for matrices/frames, - // for scalars, we compile (s + "") to ensure consistent string output value types - currBuiltinOp = !paramHops.get("target").getDataType().isScalar() ? new ParameterizedBuiltinOp( - target.getName(), target.getDataType(), target.getValueType(), ParamBuiltinOp.TOSTRING, - paramHops) : HopRewriteUtils.createBinary(paramHops.get("target"), new LiteralOp(""), OpOp2.PLUS); + //check for input data type and only compile toString Hop for matrices/frames, + //for scalars, we compile (s + "") to ensure consistent string output value types + currBuiltinOp = !paramHops.get("target").getDataType().isScalar() ? + new ParameterizedBuiltinOp(target.getName(), target.getDataType(), + target.getValueType(), ParamBuiltinOp.TOSTRING, paramHops) : + HopRewriteUtils.createBinary(paramHops.get("target"), new LiteralOp(""), OpOp2.PLUS); break; case LISTNV: @@ -2072,39 +2041,37 @@ private Hop processParameterizedBuiltinFunctionExpression(ParameterizedBuiltinFu case COUNT_DISTINCT: case COUNT_DISTINCT_APPROX: { - Direction dir = Direction.RowCol; // Default direction - DataType dataType = DataType.SCALAR; // Default output data type + Direction dir = Direction.RowCol; // Default direction + DataType dataType = DataType.SCALAR; // Default output data type LiteralOp dirOp = (LiteralOp) paramHops.get("dir"); - if(dirOp != null) { + if (dirOp != null) { String dirString = dirOp.getStringValue().toUpperCase(); - if(dirString.equals(Direction.RowCol.toString())) { + if (dirString.equals(Direction.RowCol.toString())) { dir = Direction.RowCol; dataType = DataType.SCALAR; - } - else if(dirString.equals(Direction.Row.toString())) { + } else if (dirString.equals(Direction.Row.toString())) { dir = Direction.Row; dataType = DataType.MATRIX; - } - else if(dirString.equals(Direction.Col.toString())) { + } else if (dirString.equals(Direction.Col.toString())) { dir = Direction.Col; dataType = DataType.MATRIX; } } currBuiltinOp = new AggUnaryOp(target.getName(), dataType, target.getValueType(), - AggOp.valueOf(source.getOpCode().name()), dir, paramHops.get("data")); + AggOp.valueOf(source.getOpCode().name()), dir, paramHops.get("data")); break; } case COUNT_DISTINCT_APPROX_ROW: currBuiltinOp = new AggUnaryOp(target.getName(), DataType.MATRIX, target.getValueType(), - AggOp.valueOf(source.getOpCode().name()), Direction.Row, paramHops.get("data")); + AggOp.valueOf(source.getOpCode().name()), Direction.Row, paramHops.get("data")); break; case COUNT_DISTINCT_APPROX_COL: currBuiltinOp = new AggUnaryOp(target.getName(), DataType.MATRIX, target.getValueType(), - AggOp.valueOf(source.getOpCode().name()), Direction.Col, paramHops.get("data")); + AggOp.valueOf(source.getOpCode().name()), Direction.Col, paramHops.get("data")); break; case UNIQUE: @@ -2112,26 +2079,24 @@ else if(dirString.equals(Direction.Col.toString())) { DataType dataType = DataType.MATRIX; LiteralOp dirOp = (LiteralOp) paramHops.get("dir"); - if(dirOp != null) { + if (dirOp != null) { String dirString = dirOp.getStringValue().toUpperCase(); - if(dirString.equals(Direction.RowCol.toString())) { + if (dirString.equals(Direction.RowCol.toString())) { dir = Direction.RowCol; - } - else if(dirString.equals(Direction.Row.toString())) { + } else if (dirString.equals(Direction.Row.toString())) { dir = Direction.Row; - } - else if(dirString.equals(Direction.Col.toString())) { + } else if (dirString.equals(Direction.Col.toString())) { dir = Direction.Col; } } currBuiltinOp = new AggUnaryOp(target.getName(), dataType, target.getValueType(), - AggOp.valueOf(source.getOpCode().name()), dir, paramHops.get("data")); + AggOp.valueOf(source.getOpCode().name()), dir, paramHops.get("data")); break; default: - throw new ParseException(source.printErrorLocation() - + "processParameterizedBuiltinFunctionExpression() -- Unknown operation: " + source.getOpCode()); + throw new ParseException(source.printErrorLocation() + + "processParameterizedBuiltinFunctionExpression() -- Unknown operation: " + source.getOpCode()); } setIdentifierParams(currBuiltinOp, source.getOutput()); @@ -2140,96 +2105,96 @@ else if(dirString.equals(Direction.Col.toString())) { } /** - * Construct Hops from parse tree : Process ParameterizedExpression in a read/write/rand statement + * Construct Hops from parse tree : Process ParameterizedExpression in a + * read/write/rand statement * * @param source data expression * @param target data identifier - * @param hops map of high-level operators + * @param hops map of high-level operators * @return high-level operator */ - private Hop processDataExpression(DataExpression source, DataIdentifier target, HashMap hops) { + private Hop processDataExpression(DataExpression source, DataIdentifier target, + HashMap hops) { // this expression has multiple "named" parameters HashMap paramHops = new HashMap<>(); // -- construct hops for all input parameters // -- store them in hashmap so that their "name"s are maintained - Hop pHop = null; - for(String paramName : source.getVarParams().keySet()) { + Hop pHop = null; + for ( String paramName : source.getVarParams().keySet() ) { pHop = processExpression(source.getVarParam(paramName), null, hops); paramHops.put(paramName, pHop); } Hop currBuiltinOp = null; - if(target == null) { + if (target == null) { target = createTarget(source); } // construct hop based on opcode switch(source.getOpCode()) { - case READ: - currBuiltinOp = new DataOp(target.getName(), target.getDataType(), target.getValueType(), - OpOpData.PERSISTENTREAD, paramHops); - ((DataOp) currBuiltinOp) - .setFileName(((StringIdentifier) source.getVarParam(DataExpression.IO_FILENAME)).getValue()); - break; - - case WRITE: - currBuiltinOp = new DataOp(target.getName(), target.getDataType(), target.getValueType(), - OpOpData.PERSISTENTWRITE, hops.get(target.getName()), paramHops); - break; - - case RAND: - // We limit RAND_MIN, RAND_MAX, RAND_SPARSITY, RAND_SEED, and RAND_PDF to be constants - OpOpDG method = (paramHops.get(DataExpression.RAND_MIN).getValueType() == ValueType.STRING && + case READ: + currBuiltinOp = new DataOp(target.getName(), target.getDataType(), target.getValueType(), OpOpData.PERSISTENTREAD, paramHops); + ((DataOp)currBuiltinOp).setFileName(((StringIdentifier)source.getVarParam(DataExpression.IO_FILENAME)).getValue()); + break; + + case WRITE: + currBuiltinOp = new DataOp(target.getName(), target.getDataType(), target.getValueType(), + OpOpData.PERSISTENTWRITE, hops.get(target.getName()), paramHops); + break; + + case RAND: + // We limit RAND_MIN, RAND_MAX, RAND_SPARSITY, RAND_SEED, and RAND_PDF to be constants + OpOpDG method = (paramHops.get(DataExpression.RAND_MIN).getValueType()==ValueType.STRING && target.getDataType() == DataType.MATRIX) ? OpOpDG.SINIT : OpOpDG.RAND; - currBuiltinOp = new DataGenOp(method, target, paramHops); - break; - - case FRAME: - // We limit RAND_MIN, RAND_MAX, RAND_SPARSITY, RAND_SEED, and RAND_PDF to be constants - method = OpOpDG.FRAMEINIT; - currBuiltinOp = new DataGenOp(method, target, paramHops); - break; - - case TENSOR: - case MATRIX: - ArrayList tmpMatrix = new ArrayList<>(); - tmpMatrix.add(0, paramHops.get(DataExpression.RAND_DATA)); - tmpMatrix.add(1, paramHops.get(DataExpression.RAND_ROWS)); - tmpMatrix.add(2, paramHops.get(DataExpression.RAND_COLS)); - tmpMatrix.add(3, !paramHops.containsKey(DataExpression.RAND_DIMS) ? new LiteralOp("-1") : paramHops - .get(DataExpression.RAND_DIMS)); - tmpMatrix.add(4, paramHops.get(DataExpression.RAND_BY_ROW)); - currBuiltinOp = new ReorgOp(target.getName(), target.getDataType(), target.getValueType(), - ReOrgOp.RESHAPE, tmpMatrix); - break; - - case SQL: - currBuiltinOp = new DataOp(target.getName(), target.getDataType(), target.getValueType(), - OpOpData.SQLREAD, paramHops); - break; - - case FEDERATED: - currBuiltinOp = new DataOp(target.getName(), target.getDataType(), target.getValueType(), - OpOpData.FEDERATED, paramHops); - break; - - default: - throw new ParseException(source.printErrorLocation() + "processDataExpression():: Unknown operation: " - + source.getOpCode()); - } - - // set identifier meta data (incl dimensions and blocksizes) + currBuiltinOp = new DataGenOp(method, target, paramHops); + break; + + case FRAME: + // We limit RAND_MIN, RAND_MAX, RAND_SPARSITY, RAND_SEED, and RAND_PDF to be constants + method = OpOpDG.FRAMEINIT; + currBuiltinOp = new DataGenOp(method, target, paramHops); + break; + + case TENSOR: + case MATRIX: + ArrayList tmpMatrix = new ArrayList<>(); + tmpMatrix.add( 0, paramHops.get(DataExpression.RAND_DATA) ); + tmpMatrix.add( 1, paramHops.get(DataExpression.RAND_ROWS) ); + tmpMatrix.add( 2, paramHops.get(DataExpression.RAND_COLS) ); + tmpMatrix.add( 3, !paramHops.containsKey(DataExpression.RAND_DIMS) ? + new LiteralOp("-1") : paramHops.get(DataExpression.RAND_DIMS)); + tmpMatrix.add( 4, paramHops.get(DataExpression.RAND_BY_ROW) ); + currBuiltinOp = new ReorgOp(target.getName(), target.getDataType(), + target.getValueType(), ReOrgOp.RESHAPE, tmpMatrix); + break; + + case SQL: + currBuiltinOp = new DataOp(target.getName(), target.getDataType(), + target.getValueType(), OpOpData.SQLREAD, paramHops); + break; + + case FEDERATED: + currBuiltinOp = new DataOp(target.getName(), target.getDataType(), + target.getValueType(), OpOpData.FEDERATED, paramHops); + break; + + default: + throw new ParseException(source.printErrorLocation() + + "processDataExpression():: Unknown operation: " + source.getOpCode()); + } + + //set identifier meta data (incl dimensions and blocksizes) setIdentifierParams(currBuiltinOp, source.getOutput()); - if(source.getOpCode() == DataExpression.DataOp.READ) - ((DataOp) currBuiltinOp).setInputBlocksize(target.getBlocksize()); - else if(source.getOpCode() == DataExpression.DataOp.WRITE) { - ((DataOp) currBuiltinOp).setPrivacy(hops.get(target.getName()).getPrivacy()); - if(source.getVarParam(DataExpression.ROWBLOCKCOUNTPARAM) != null) - currBuiltinOp - .setBlocksize(Integer.parseInt(source.getVarParam(DataExpression.ROWBLOCKCOUNTPARAM).toString())); + if( source.getOpCode()==DataExpression.DataOp.READ ) + ((DataOp)currBuiltinOp).setInputBlocksize(target.getBlocksize()); + else if ( source.getOpCode() == DataExpression.DataOp.WRITE ) { + ((DataOp)currBuiltinOp).setPrivacy(hops.get(target.getName()).getPrivacy()); + if( source.getVarParam(DataExpression.ROWBLOCKCOUNTPARAM) != null ) + currBuiltinOp.setBlocksize(Integer.parseInt( + source.getVarParam(DataExpression.ROWBLOCKCOUNTPARAM).toString())); } currBuiltinOp.setParseInfo(source); @@ -2237,24 +2202,25 @@ else if(source.getOpCode() == DataExpression.DataOp.WRITE) { } /** - * Construct HOps from parse tree: process BuiltinFunction Expressions in MultiAssignment Statements. For all other - * builtin function expressions, processBuiltinFunctionExpression() is used. + * Construct HOps from parse tree: process BuiltinFunction Expressions in + * MultiAssignment Statements. For all other builtin function expressions, + * processBuiltinFunctionExpression() is used. * - * @param source built-in function expression + * @param source built-in function expression * @param targetList list of data identifiers - * @param hops map of high-level operators + * @param hops map of high-level operators * @return high-level operator */ - private Hop processMultipleReturnBuiltinFunctionExpression(BuiltinFunctionExpression source, - ArrayList targetList, HashMap hops) { + private Hop processMultipleReturnBuiltinFunctionExpression(BuiltinFunctionExpression source, ArrayList targetList, + HashMap hops) { // Construct Hops for all inputs ArrayList inputs = new ArrayList<>(); - inputs.add(processExpression(source.getFirstExpr(), null, hops)); + inputs.add( processExpression(source.getFirstExpr(), null, hops) ); Expression[] expr = source.getAllExpr(); if(expr != null && expr.length > 1) { for(int i = 1; i < expr.length; i++) { - inputs.add(processExpression(expr[i], null, hops)); + inputs.add( processExpression(expr[i], null, hops) ); } } @@ -2267,7 +2233,7 @@ private Hop processMultipleReturnBuiltinFunctionExpression(BuiltinFunctionExpres // Construct Hop for current builtin function expression based on its type Hop currBuiltinOp = null; - switch(source.getOpCode()) { + switch (source.getOpCode()) { case QR: case LU: case EIGEN: @@ -2281,41 +2247,34 @@ private Hop processMultipleReturnBuiltinFunctionExpression(BuiltinFunctionExpres case SVD: // Number of outputs = size of targetList = #of identifiers in source.getOutputs - String[] outputNames = new String[targetList.size()]; - for(int i = 0; i < targetList.size(); i++) { + String[] outputNames = new String[targetList.size()]; + for ( int i=0; i < targetList.size(); i++ ) { outputNames[i] = targetList.get(i).getName(); - Hop output = new DataOp(outputNames[i], DataType.MATRIX, ValueType.FP64, inputs.get(0), - OpOpData.FUNCTIONOUTPUT, inputs.get(0).getFilename()); + Hop output = new DataOp(outputNames[i], DataType.MATRIX, ValueType.FP64, inputs.get(0), OpOpData.FUNCTIONOUTPUT, inputs.get(0).getFilename()); outputs.add(output); } // Create the hop for current function call - currBuiltinOp = new FunctionOp(ftype, nameSpace, source.getOpCode().toString(), null, inputs, - outputNames, outputs); + currBuiltinOp = new FunctionOp(ftype, nameSpace, source.getOpCode().toString(), null, inputs, outputNames, outputs); break; case COMPRESS: // Number of outputs = size of targetList = #of identifiers in source.getOutputs String[] outputNamesCompress = new String[targetList.size()]; outputNamesCompress[0] = targetList.get(0).getName(); outputNamesCompress[1] = targetList.get(1).getName(); - outputs.add(new DataOp(outputNamesCompress[0], DataType.MATRIX, ValueType.FP64, inputs.get(0), - OpOpData.FUNCTIONOUTPUT, inputs.get(0).getFilename())); - outputs.add(new DataOp(outputNamesCompress[1], DataType.FRAME, ValueType.STRING, inputs.get(0), - OpOpData.FUNCTIONOUTPUT, inputs.get(0).getFilename())); + outputs.add(new DataOp(outputNamesCompress[0], DataType.MATRIX, ValueType.FP64, inputs.get(0), OpOpData.FUNCTIONOUTPUT, inputs.get(0).getFilename())); + outputs.add(new DataOp(outputNamesCompress[1], DataType.FRAME, ValueType.STRING, inputs.get(0), OpOpData.FUNCTIONOUTPUT, inputs.get(0).getFilename())); // Create the hop for current function call - currBuiltinOp = new FunctionOp(ftype, nameSpace, source.getOpCode().toString(), null, inputs, - outputNamesCompress, outputs); + currBuiltinOp = new FunctionOp(ftype, nameSpace, source.getOpCode().toString(), null, inputs, outputNamesCompress, outputs); break; default: - throw new ParseException( - "Invaid Opcode in DMLTranslator:processMultipleReturnBuiltinFunctionExpression(): " - + source.getOpCode()); + throw new ParseException("Invaid Opcode in DMLTranslator:processMultipleReturnBuiltinFunctionExpression(): " + source.getOpCode()); } // set properties for created hops based on outputs of source expression - for(int i = 0; i < source.getOutputs().length; i++) { - setIdentifierParams(outputs.get(i), source.getOutputs()[i]); + for ( int i=0; i < source.getOutputs().length; i++ ) { + setIdentifierParams( outputs.get(i), source.getOutputs()[i]); outputs.get(i).setParseInfo(source); } currBuiltinOp.setParseInfo(source); @@ -2324,25 +2283,26 @@ private Hop processMultipleReturnBuiltinFunctionExpression(BuiltinFunctionExpres } /** - * Construct Hops from parse tree : Process BuiltinFunction Expression in an assignment statement + * Construct Hops from parse tree : Process BuiltinFunction Expression in an + * assignment statement * * @param source built-in function expression * @param target data identifier - * @param hops map of high-level operators + * @param hops map of high-level operators * @return high-level operator */ private Hop processBuiltinFunctionExpression(BuiltinFunctionExpression source, DataIdentifier target, - HashMap hops) { + HashMap hops) { Hop expr = null; - if(source.getFirstExpr() != null) { + if(source.getFirstExpr() != null){ expr = processExpression(source.getFirstExpr(), null, hops); } Hop expr2 = null; - if(source.getSecondExpr() != null) { + if (source.getSecondExpr() != null) { expr2 = processExpression(source.getSecondExpr(), null, hops); } Hop expr3 = null; - if(source.getThirdExpr() != null) { + if (source.getThirdExpr() != null) { expr3 = processExpression(source.getThirdExpr(), null, hops); } @@ -2350,519 +2310,506 @@ private Hop processBuiltinFunctionExpression(BuiltinFunctionExpression source, D target = (target == null) ? createTarget(source) : target; // Construct the hop based on the type of Builtin function - switch(source.getOpCode()) { - - case EVAL: - case EVALLIST: - currBuiltinOp = new NaryOp(target.getName(), target.getDataType(), target.getValueType(), OpOpN.EVAL, - processAllExpressions(source.getAllExpr(), hops)); - break; - - case COLSUM: - case COLMAX: - case COLMIN: - case COLMEAN: - case COLPROD: - case COLVAR: - currBuiltinOp = new AggUnaryOp(target.getName(), DataType.MATRIX, target.getValueType(), - AggOp.valueOf(source.getOpCode().name().substring(3)), Direction.Col, expr); - break; - - case COLSD: - // colStdDevs = sqrt(colVariances) - currBuiltinOp = new AggUnaryOp(target.getName(), DataType.MATRIX, target.getValueType(), AggOp.VAR, - Direction.Col, expr); - currBuiltinOp = new UnaryOp(target.getName(), DataType.MATRIX, target.getValueType(), OpOp1.SQRT, - currBuiltinOp); - break; - - case ROWSUM: - case ROWMIN: - case ROWMAX: - case ROWMEAN: - case ROWPROD: - case ROWVAR: - currBuiltinOp = new AggUnaryOp(target.getName(), DataType.MATRIX, target.getValueType(), - AggOp.valueOf(source.getOpCode().name().substring(3)), Direction.Row, expr); - break; - - case ROWINDEXMAX: - currBuiltinOp = new AggUnaryOp(target.getName(), DataType.MATRIX, target.getValueType(), AggOp.MAXINDEX, + switch (source.getOpCode()) { + + case EVAL: + case EVALLIST: + currBuiltinOp = new NaryOp(target.getName(), target.getDataType(), target.getValueType(), + OpOpN.EVAL, processAllExpressions(source.getAllExpr(), hops)); + break; + + case COLSUM: + case COLMAX: + case COLMIN: + case COLMEAN: + case COLPROD: + case COLVAR: + currBuiltinOp = new AggUnaryOp(target.getName(), DataType.MATRIX, target.getValueType(), + AggOp.valueOf(source.getOpCode().name().substring(3)), Direction.Col, expr); + break; + + case COLSD: + // colStdDevs = sqrt(colVariances) + currBuiltinOp = new AggUnaryOp(target.getName(), DataType.MATRIX, + target.getValueType(), AggOp.VAR, Direction.Col, expr); + currBuiltinOp = new UnaryOp(target.getName(), DataType.MATRIX, + target.getValueType(), OpOp1.SQRT, currBuiltinOp); + break; + + case ROWSUM: + case ROWMIN: + case ROWMAX: + case ROWMEAN: + case ROWPROD: + case ROWVAR: + currBuiltinOp = new AggUnaryOp(target.getName(), DataType.MATRIX, target.getValueType(), + AggOp.valueOf(source.getOpCode().name().substring(3)), Direction.Row, expr); + break; + + case ROWINDEXMAX: + currBuiltinOp = new AggUnaryOp(target.getName(), DataType.MATRIX, target.getValueType(), AggOp.MAXINDEX, Direction.Row, expr); - break; + break; - case ROWINDEXMIN: - currBuiltinOp = new AggUnaryOp(target.getName(), DataType.MATRIX, target.getValueType(), AggOp.MININDEX, + case ROWINDEXMIN: + currBuiltinOp = new AggUnaryOp(target.getName(), DataType.MATRIX, target.getValueType(), AggOp.MININDEX, Direction.Row, expr); - break; - - case ROWSD: - // rowStdDevs = sqrt(rowVariances) - currBuiltinOp = new AggUnaryOp(target.getName(), DataType.MATRIX, target.getValueType(), AggOp.VAR, - Direction.Row, expr); - currBuiltinOp = new UnaryOp(target.getName(), DataType.MATRIX, target.getValueType(), OpOp1.SQRT, - currBuiltinOp); - break; - - case NROW: - // If the dimensions are available at compile time, then create a LiteralOp (constant propagation) - // Else create a UnaryOp so that a control program instruction is generated - currBuiltinOp = (expr.getDim1() == -1) ? new UnaryOp(target.getName(), target.getDataType(), - target.getValueType(), OpOp1.NROW, expr) : new LiteralOp(expr.getDim1()); - break; - case NCOL: - // If the dimensions are available at compile time, then create a LiteralOp (constant propagation) - // Else create a UnaryOp so that a control program instruction is generated - currBuiltinOp = (expr.getDim2() == -1) ? new UnaryOp(target.getName(), target.getDataType(), - target.getValueType(), OpOp1.NCOL, expr) : new LiteralOp(expr.getDim2()); - break; - case LENGTH: - // If the dimensions are available at compile time, then create a LiteralOp (constant propagation) - // Else create a UnaryOp so that a control program instruction is generated - currBuiltinOp = (expr.getDim1() == -1 || expr.getDim2() == -1) ? new UnaryOp(target.getName(), - target.getDataType(), target.getValueType(), OpOp1.LENGTH, - expr) : new LiteralOp(expr.getDim1() * expr.getDim2()); - break; + break; + + case ROWSD: + // rowStdDevs = sqrt(rowVariances) + currBuiltinOp = new AggUnaryOp(target.getName(), DataType.MATRIX, + target.getValueType(), AggOp.VAR, Direction.Row, expr); + currBuiltinOp = new UnaryOp(target.getName(), DataType.MATRIX, + target.getValueType(), OpOp1.SQRT, currBuiltinOp); + break; + + case NROW: + // If the dimensions are available at compile time, then create a LiteralOp (constant propagation) + // Else create a UnaryOp so that a control program instruction is generated + currBuiltinOp = (expr.getDim1()==-1) ? new UnaryOp(target.getName(), target.getDataType(), + target.getValueType(), OpOp1.NROW, expr) : new LiteralOp(expr.getDim1()); + break; + case NCOL: + // If the dimensions are available at compile time, then create a LiteralOp (constant propagation) + // Else create a UnaryOp so that a control program instruction is generated + currBuiltinOp = (expr.getDim2()==-1) ? new UnaryOp(target.getName(), target.getDataType(), + target.getValueType(), OpOp1.NCOL, expr) : new LiteralOp(expr.getDim2()); + break; + case LENGTH: + // If the dimensions are available at compile time, then create a LiteralOp (constant propagation) + // Else create a UnaryOp so that a control program instruction is generated + currBuiltinOp = (expr.getDim1()==-1 || expr.getDim2()==-1) ? new UnaryOp(target.getName(), target.getDataType(), + target.getValueType(), OpOp1.LENGTH, expr) : new LiteralOp(expr.getDim1()*expr.getDim2()); + break; + + case LINEAGE: + //construct hop and enable lineage tracing if necessary + currBuiltinOp = new UnaryOp(target.getName(), target.getDataType(), + target.getValueType(), OpOp1.LINEAGE, expr); + DMLScript.LINEAGE = true; + break; + + case LIST: + currBuiltinOp = new NaryOp(target.getName(), DataType.LIST, ValueType.UNKNOWN, + OpOpN.LIST, processAllExpressions(source.getAllExpr(), hops)); + break; + + case EXISTS: + currBuiltinOp = new UnaryOp(target.getName(), DataType.SCALAR, + target.getValueType(), OpOp1.EXISTS, expr); + break; + + case SUM: + case PROD: + case VAR: + currBuiltinOp = new AggUnaryOp(target.getName(), DataType.SCALAR, target.getValueType(), + AggOp.valueOf(source.getOpCode().name()), Direction.RowCol, expr); + break; - case LINEAGE: - // construct hop and enable lineage tracing if necessary - currBuiltinOp = new UnaryOp(target.getName(), target.getDataType(), target.getValueType(), - OpOp1.LINEAGE, expr); - DMLScript.LINEAGE = true; - break; + case MEAN: + if ( expr2 == null ) { + // example: x = mean(Y); + currBuiltinOp = new AggUnaryOp(target.getName(), DataType.SCALAR, target.getValueType(), AggOp.MEAN, + Direction.RowCol, expr); + } + else { + // example: x = mean(Y,W); + // stable weighted mean is implemented by using centralMoment with order = 0 + Hop orderHop = new LiteralOp(0); + currBuiltinOp=new TernaryOp(target.getName(), DataType.SCALAR, + target.getValueType(), OpOp3.MOMENT, expr, expr2, orderHop); + } + break; + + case SD: + // stdDev = sqrt(variance) + currBuiltinOp = new AggUnaryOp(target.getName(), DataType.SCALAR, + target.getValueType(), AggOp.VAR, Direction.RowCol, expr); + HopRewriteUtils.setOutputParametersForScalar(currBuiltinOp); + currBuiltinOp = new UnaryOp(target.getName(), DataType.SCALAR, + target.getValueType(), OpOp1.SQRT, currBuiltinOp); + break; + + case MIN: + case MAX: + //construct AggUnary for min(X) but BinaryOp for min(X,Y) and NaryOp for min(X,Y,Z) + currBuiltinOp = (expr2 == null) ? + new AggUnaryOp(target.getName(), DataType.SCALAR, target.getValueType(), + AggOp.valueOf(source.getOpCode().name()), Direction.RowCol, expr) : + (source.getAllExpr().length == 2) ? + new BinaryOp(target.getName(), target.getDataType(), target.getValueType(), + OpOp2.valueOf(source.getOpCode().name()), expr, expr2) : + new NaryOp(target.getName(), target.getDataType(), target.getValueType(), + OpOpN.valueOf(source.getOpCode().name()), processAllExpressions(source.getAllExpr(), hops)); + break; + + case PPRED: + String sop = ((StringIdentifier)source.getThirdExpr()).getValue(); + sop = sop.replace("\"", ""); + OpOp2 operation; + if ( sop.equalsIgnoreCase(">=") ) + operation = OpOp2.GREATEREQUAL; + else if ( sop.equalsIgnoreCase(">") ) + operation = OpOp2.GREATER; + else if ( sop.equalsIgnoreCase("<=") ) + operation = OpOp2.LESSEQUAL; + else if ( sop.equalsIgnoreCase("<") ) + operation = OpOp2.LESS; + else if ( sop.equalsIgnoreCase("==") ) + operation = OpOp2.EQUAL; + else if ( sop.equalsIgnoreCase("!=") ) + operation = OpOp2.NOTEQUAL; + else { + throw new ParseException(source.printErrorLocation() + "Unknown argument (" + sop + ") for PPRED."); + } + currBuiltinOp = new BinaryOp(target.getName(), target.getDataType(), target.getValueType(), operation, expr, expr2); + break; - case LIST: - currBuiltinOp = new NaryOp(target.getName(), DataType.LIST, ValueType.UNKNOWN, OpOpN.LIST, + case TRACE: + currBuiltinOp = new AggUnaryOp(target.getName(), DataType.SCALAR, target.getValueType(), AggOp.TRACE, + Direction.RowCol, expr); + break; + + case TRANS: + case DIAG: + case REV: + currBuiltinOp = new ReorgOp(target.getName(), DataType.MATRIX, + target.getValueType(), ReOrgOp.valueOf(source.getOpCode().name()), expr); + break; + + case CBIND: + case RBIND: + OpOp2 appendOp2 = (source.getOpCode()==Builtins.CBIND) ? OpOp2.CBIND : OpOp2.RBIND; + OpOpN appendOpN = (source.getOpCode()==Builtins.CBIND) ? OpOpN.CBIND : OpOpN.RBIND; + currBuiltinOp = (source.getAllExpr().length == 2) ? + new BinaryOp(target.getName(), target.getDataType(), target.getValueType(), appendOp2, expr, expr2) : + new NaryOp(target.getName(), target.getDataType(), target.getValueType(), appendOpN, processAllExpressions(source.getAllExpr(), hops)); - break; - - case EXISTS: - currBuiltinOp = new UnaryOp(target.getName(), DataType.SCALAR, target.getValueType(), OpOp1.EXISTS, - expr); - break; - - case SUM: - case PROD: - case VAR: - currBuiltinOp = new AggUnaryOp(target.getName(), DataType.SCALAR, target.getValueType(), - AggOp.valueOf(source.getOpCode().name()), Direction.RowCol, expr); - break; - - case MEAN: - if(expr2 == null) { - // example: x = mean(Y); - currBuiltinOp = new AggUnaryOp(target.getName(), DataType.SCALAR, target.getValueType(), AggOp.MEAN, - Direction.RowCol, expr); - } + break; + + case TABLE: + + // Always a TertiaryOp is created for table(). + // - create a hop for weights, if not provided in the function call. + int numTableArgs = source._args.length; + + switch(numTableArgs) { + case 2: + case 4: + // example DML statement: F = ctable(A,B) or F = ctable(A,B,10,15) + // here, weight is interpreted as 1.0 + Hop weightHop = new LiteralOp(1.0); + // set dimensions + weightHop.setDim1(0); + weightHop.setDim2(0); + weightHop.setNnz(-1); + weightHop.setBlocksize(0); + + if ( numTableArgs == 2 ) + currBuiltinOp = new TernaryOp(target.getName(), target.getDataType(), target.getValueType(), OpOp3.CTABLE, expr, expr2, weightHop); else { - // example: x = mean(Y,W); - // stable weighted mean is implemented by using centralMoment with order = 0 - Hop orderHop = new LiteralOp(0); - currBuiltinOp = new TernaryOp(target.getName(), DataType.SCALAR, target.getValueType(), - OpOp3.MOMENT, expr, expr2, orderHop); + Hop outDim1 = processExpression(source._args[2], null, hops); + Hop outDim2 = processExpression(source._args[3], null, hops); + currBuiltinOp = new TernaryOp(target.getName(), target.getDataType(), target.getValueType(), + OpOp3.CTABLE, expr, expr2, weightHop, outDim1, outDim2, new LiteralOp(true)); } break; - case SD: - // stdDev = sqrt(variance) - currBuiltinOp = new AggUnaryOp(target.getName(), DataType.SCALAR, target.getValueType(), AggOp.VAR, - Direction.RowCol, expr); - HopRewriteUtils.setOutputParametersForScalar(currBuiltinOp); - currBuiltinOp = new UnaryOp(target.getName(), DataType.SCALAR, target.getValueType(), OpOp1.SQRT, - currBuiltinOp); - break; - - case MIN: - case MAX: - // construct AggUnary for min(X) but BinaryOp for min(X,Y) and NaryOp for min(X,Y,Z) - currBuiltinOp = (expr2 == null) ? new AggUnaryOp(target.getName(), DataType.SCALAR, - target.getValueType(), AggOp.valueOf(source.getOpCode().name()), Direction.RowCol, - expr) : (source.getAllExpr().length == 2) ? new BinaryOp(target.getName(), target.getDataType(), - target.getValueType(), OpOp2.valueOf(source.getOpCode().name()), expr, - expr2) : new NaryOp(target.getName(), target.getDataType(), target.getValueType(), - OpOpN.valueOf(source.getOpCode().name()), processAllExpressions(source.getAllExpr(), hops)); - break; - - case PPRED: - String sop = ((StringIdentifier) source.getThirdExpr()).getValue(); - sop = sop.replace("\"", ""); - OpOp2 operation; - if(sop.equalsIgnoreCase(">=")) - operation = OpOp2.GREATEREQUAL; - else if(sop.equalsIgnoreCase(">")) - operation = OpOp2.GREATER; - else if(sop.equalsIgnoreCase("<=")) - operation = OpOp2.LESSEQUAL; - else if(sop.equalsIgnoreCase("<")) - operation = OpOp2.LESS; - else if(sop.equalsIgnoreCase("==")) - operation = OpOp2.EQUAL; - else if(sop.equalsIgnoreCase("!=")) - operation = OpOp2.NOTEQUAL; + case 3: + case 5: + case 6: + // example DML statement: F = ctable(A,B,W) or F = ctable(A,B,W,10,15) + if (numTableArgs == 3) + currBuiltinOp = new TernaryOp(target.getName(), target.getDataType(), target.getValueType(), OpOp3.CTABLE, expr, expr2, expr3); else { - throw new ParseException(source.printErrorLocation() + "Unknown argument (" + sop + ") for PPRED."); + Hop outDim1 = processExpression(source._args[3], null, hops); + Hop outDim2 = processExpression(source._args[4], null, hops); + Hop outputEmptyBlocks = numTableArgs == 6 ? + processExpression(source._args[5], null, hops) : new LiteralOp(true); + currBuiltinOp = new TernaryOp(target.getName(), target.getDataType(), target.getValueType(), + OpOp3.CTABLE, expr, expr2, expr3, outDim1, outDim2, outputEmptyBlocks); } - currBuiltinOp = new BinaryOp(target.getName(), target.getDataType(), target.getValueType(), operation, - expr, expr2); - break; - - case TRACE: - currBuiltinOp = new AggUnaryOp(target.getName(), DataType.SCALAR, target.getValueType(), AggOp.TRACE, - Direction.RowCol, expr); - break; - - case TRANS: - case DIAG: - case REV: - currBuiltinOp = new ReorgOp(target.getName(), DataType.MATRIX, target.getValueType(), - ReOrgOp.valueOf(source.getOpCode().name()), expr); break; - case CBIND: - case RBIND: - OpOp2 appendOp2 = (source.getOpCode() == Builtins.CBIND) ? OpOp2.CBIND : OpOp2.RBIND; - OpOpN appendOpN = (source.getOpCode() == Builtins.CBIND) ? OpOpN.CBIND : OpOpN.RBIND; - currBuiltinOp = (source.getAllExpr().length == 2) ? new BinaryOp(target.getName(), target.getDataType(), - target.getValueType(), appendOp2, expr, expr2) : new NaryOp(target.getName(), target.getDataType(), - target.getValueType(), appendOpN, processAllExpressions(source.getAllExpr(), hops)); - break; - - case TABLE: - - // Always a TertiaryOp is created for table(). - // - create a hop for weights, if not provided in the function call. - int numTableArgs = source._args.length; - - switch(numTableArgs) { - case 2: - case 4: - // example DML statement: F = ctable(A,B) or F = ctable(A,B,10,15) - // here, weight is interpreted as 1.0 - Hop weightHop = new LiteralOp(1.0); - // set dimensions - weightHop.setDim1(0); - weightHop.setDim2(0); - weightHop.setNnz(-1); - weightHop.setBlocksize(0); - - if(numTableArgs == 2) - currBuiltinOp = new TernaryOp(target.getName(), target.getDataType(), target.getValueType(), - OpOp3.CTABLE, expr, expr2, weightHop); - else { - Hop outDim1 = processExpression(source._args[2], null, hops); - Hop outDim2 = processExpression(source._args[3], null, hops); - currBuiltinOp = new TernaryOp(target.getName(), target.getDataType(), target.getValueType(), - OpOp3.CTABLE, expr, expr2, weightHop, outDim1, outDim2, new LiteralOp(true)); - } - break; + default: + throw new ParseException("Invalid number of arguments "+ numTableArgs + " to table() function."); + } + break; - case 3: - case 5: - case 6: - // example DML statement: F = ctable(A,B,W) or F = ctable(A,B,W,10,15) - if(numTableArgs == 3) - currBuiltinOp = new TernaryOp(target.getName(), target.getDataType(), target.getValueType(), - OpOp3.CTABLE, expr, expr2, expr3); - else { - Hop outDim1 = processExpression(source._args[3], null, hops); - Hop outDim2 = processExpression(source._args[4], null, hops); - Hop outputEmptyBlocks = numTableArgs == 6 ? processExpression(source._args[5], null, - hops) : new LiteralOp(true); - currBuiltinOp = new TernaryOp(target.getName(), target.getDataType(), target.getValueType(), - OpOp3.CTABLE, expr, expr2, expr3, outDim1, outDim2, outputEmptyBlocks); - } + //data type casts + case CAST_AS_SCALAR: + currBuiltinOp = new UnaryOp(target.getName(), DataType.SCALAR, target.getValueType(), OpOp1.CAST_AS_SCALAR, expr); + break; + case CAST_AS_MATRIX: + currBuiltinOp = new UnaryOp(target.getName(), DataType.MATRIX, target.getValueType(), OpOp1.CAST_AS_MATRIX, expr); + break; + case CAST_AS_FRAME: + if(expr2 != null) + currBuiltinOp = new BinaryOp(target.getName(), DataType.FRAME, target.getValueType(), OpOp2.CAST_AS_FRAME, expr, expr2); + else + currBuiltinOp = new UnaryOp(target.getName(), DataType.FRAME, target.getValueType(), OpOp1.CAST_AS_FRAME, expr); + break; + case CAST_AS_LIST: + currBuiltinOp = new UnaryOp(target.getName(), DataType.LIST, target.getValueType(), OpOp1.CAST_AS_LIST, expr); + break; + + //value type casts + case CAST_AS_DOUBLE: + currBuiltinOp = new UnaryOp(target.getName(), target.getDataType(), ValueType.FP64, OpOp1.CAST_AS_DOUBLE, expr); + break; + case CAST_AS_INT: + currBuiltinOp = new UnaryOp(target.getName(), target.getDataType(), ValueType.INT64, OpOp1.CAST_AS_INT, expr); + break; + case CAST_AS_BOOLEAN: + currBuiltinOp = new UnaryOp(target.getName(), target.getDataType(), ValueType.BOOLEAN, OpOp1.CAST_AS_BOOLEAN, expr); + break; + case LOCAL: + currBuiltinOp = new UnaryOp(target.getName(), target.getDataType(), ValueType.FP64, OpOp1.LOCAL, expr); + break; + case COMPRESS: + currBuiltinOp = new UnaryOp(target.getName(), target.getDataType(), ValueType.FP64, OpOp1.COMPRESS, expr); + break; + case DECOMPRESS: + currBuiltinOp = new UnaryOp(target.getName(), target.getDataType(), ValueType.FP64, OpOp1.DECOMPRESS, expr); + break; + + // Boolean binary + case XOR: + case BITWAND: + case BITWOR: + case BITWXOR: + case BITWSHIFTL: + case BITWSHIFTR: + currBuiltinOp = new BinaryOp(target.getName(), target.getDataType(), + target.getValueType(), OpOp2.valueOf(source.getOpCode().name()), expr, expr2); + break; + case ABS: + case SIN: + case COS: + case TAN: + case ASIN: + case ACOS: + case ATAN: + case SINH: + case COSH: + case TANH: + case SIGN: + case SQRT: + case EXP: + case ROUND: + case CEIL: + case FLOOR: + case CUMSUM: + case CUMPROD: + case CUMSUMPROD: + case CUMMIN: + case CUMMAX: + case ISNA: + case ISNAN: + case ISINF: + currBuiltinOp = new UnaryOp(target.getName(), target.getDataType(), target.getValueType(), + OpOp1.valueOf(source.getOpCode().name()), expr); + break; + case DROP_INVALID_TYPE: + case DROP_INVALID_LENGTH: + case VALUE_SWAP: + case FRAME_ROW_REPLICATE: + case APPLY_SCHEMA: + currBuiltinOp = new BinaryOp(target.getName(), target.getDataType(), + target.getValueType(), OpOp2.valueOf(source.getOpCode().name()), expr, expr2); + break; + case MAP: + currBuiltinOp = new TernaryOp(target.getName(), target.getDataType(), + target.getValueType(), OpOp3.valueOf(source.getOpCode().name()), + expr, expr2, (expr3==null) ? new LiteralOp(0L) : expr3); + break; + + case LOG: + if (expr2 == null) { + OpOp1 mathOp2; + switch (source.getOpCode()) { + case LOG: + mathOp2 = OpOp1.LOG; break; - default: - throw new ParseException( - "Invalid number of arguments " + numTableArgs + " to table() function."); - } - break; - - // data type casts - case CAST_AS_SCALAR: - currBuiltinOp = new UnaryOp(target.getName(), DataType.SCALAR, target.getValueType(), - OpOp1.CAST_AS_SCALAR, expr); - break; - case CAST_AS_MATRIX: - currBuiltinOp = new UnaryOp(target.getName(), DataType.MATRIX, target.getValueType(), - OpOp1.CAST_AS_MATRIX, expr); - break; - case CAST_AS_FRAME: - if(expr2 != null) - currBuiltinOp = new BinaryOp(target.getName(), DataType.FRAME, target.getValueType(), - OpOp2.CAST_AS_FRAME, expr, expr2); - else - currBuiltinOp = new UnaryOp(target.getName(), DataType.FRAME, target.getValueType(), - OpOp1.CAST_AS_FRAME, expr); - break; - case CAST_AS_LIST: - currBuiltinOp = new UnaryOp(target.getName(), DataType.LIST, target.getValueType(), OpOp1.CAST_AS_LIST, - expr); - break; - - // value type casts - case CAST_AS_DOUBLE: - currBuiltinOp = new UnaryOp(target.getName(), target.getDataType(), ValueType.FP64, - OpOp1.CAST_AS_DOUBLE, expr); - break; - case CAST_AS_INT: - currBuiltinOp = new UnaryOp(target.getName(), target.getDataType(), ValueType.INT64, OpOp1.CAST_AS_INT, - expr); - break; - case CAST_AS_BOOLEAN: - currBuiltinOp = new UnaryOp(target.getName(), target.getDataType(), ValueType.BOOLEAN, - OpOp1.CAST_AS_BOOLEAN, expr); - break; - case LOCAL: - currBuiltinOp = new UnaryOp(target.getName(), target.getDataType(), ValueType.FP64, OpOp1.LOCAL, expr); - break; - case COMPRESS: - currBuiltinOp = new UnaryOp(target.getName(), target.getDataType(), ValueType.FP64, OpOp1.COMPRESS, - expr); - break; - case DECOMPRESS: - currBuiltinOp = new UnaryOp(target.getName(), target.getDataType(), ValueType.FP64, OpOp1.DECOMPRESS, - expr); - break; - - // Boolean binary - case XOR: - case BITWAND: - case BITWOR: - case BITWXOR: - case BITWSHIFTL: - case BITWSHIFTR: - currBuiltinOp = new BinaryOp(target.getName(), target.getDataType(), target.getValueType(), - OpOp2.valueOf(source.getOpCode().name()), expr, expr2); - break; - case ABS: - case SIN: - case COS: - case TAN: - case ASIN: - case ACOS: - case ATAN: - case SINH: - case COSH: - case TANH: - case SIGN: - case SQRT: - case EXP: - case ROUND: - case CEIL: - case FLOOR: - case CUMSUM: - case CUMPROD: - case CUMSUMPROD: - case CUMMIN: - case CUMMAX: - case ISNA: - case ISNAN: - case ISINF: - currBuiltinOp = new UnaryOp(target.getName(), target.getDataType(), target.getValueType(), - OpOp1.valueOf(source.getOpCode().name()), expr); - break; - case DROP_INVALID_TYPE: - case DROP_INVALID_LENGTH: - case VALUE_SWAP: - case FRAME_ROW_REPLICATE: - case APPLY_SCHEMA: - currBuiltinOp = new BinaryOp(target.getName(), target.getDataType(), target.getValueType(), - OpOp2.valueOf(source.getOpCode().name()), expr, expr2); - break; - case MAP: - currBuiltinOp = new TernaryOp(target.getName(), target.getDataType(), target.getValueType(), - OpOp3.valueOf(source.getOpCode().name()), expr, expr2, (expr3 == null) ? new LiteralOp(0L) : expr3); - break; - - case LOG: - if(expr2 == null) { - OpOp1 mathOp2; - switch(source.getOpCode()) { - case LOG: - mathOp2 = OpOp1.LOG; - break; - default: - throw new ParseException(source.printErrorLocation() - + "processBuiltinFunctionExpression():: Could not find Operation type for builtin function: " - + source.getOpCode()); + throw new ParseException(source.printErrorLocation() + + "processBuiltinFunctionExpression():: Could not find Operation type for builtin function: " + + source.getOpCode()); } - currBuiltinOp = new UnaryOp(target.getName(), target.getDataType(), target.getValueType(), mathOp2, - expr); - } - else { + currBuiltinOp = new UnaryOp(target.getName(), + target.getDataType(), target.getValueType(), mathOp2, expr); + } else { OpOp2 mathOp3; - switch(source.getOpCode()) { - case LOG: - mathOp3 = OpOp2.LOG; - break; - default: - throw new ParseException(source.printErrorLocation() - + "processBuiltinFunctionExpression():: Could not find Operation type for builtin function: " + switch (source.getOpCode()) { + case LOG: + mathOp3 = OpOp2.LOG; + break; + default: + throw new ParseException(source.printErrorLocation() + + "processBuiltinFunctionExpression():: Could not find Operation type for builtin function: " + source.getOpCode()); } currBuiltinOp = new BinaryOp(target.getName(), target.getDataType(), target.getValueType(), mathOp3, - expr, expr2); + expr, expr2); } - break; - - case MOMENT: - case COV: - case QUANTILE: - case INTERQUANTILE: - currBuiltinOp = (expr3 == null) ? new BinaryOp(target.getName(), target.getDataType(), - target.getValueType(), OpOp2.valueOf(source.getOpCode().name()), expr, - expr2) : new TernaryOp(target.getName(), target.getDataType(), target.getValueType(), - OpOp3.valueOf(source.getOpCode().name()), expr, expr2, expr3); - break; - - case IQM: - case MEDIAN: - currBuiltinOp = (expr2 == null) ? new UnaryOp(target.getName(), target.getDataType(), - target.getValueType(), OpOp1.valueOf(source.getOpCode().name()), - expr) : new BinaryOp(target.getName(), target.getDataType(), target.getValueType(), - OpOp2.valueOf(source.getOpCode().name()), expr, expr2); - break; - - case IFELSE: - currBuiltinOp = new TernaryOp(target.getName(), target.getDataType(), target.getValueType(), - OpOp3.IFELSE, expr, expr2, expr3); - break; - - case SEQ: - HashMap randParams = new HashMap<>(); - randParams.put(Statement.SEQ_FROM, expr); - randParams.put(Statement.SEQ_TO, expr2); - randParams.put(Statement.SEQ_INCR, (expr3 != null) ? expr3 : new LiteralOp(1)); - // note incr: default -1 (for from>to) handled during runtime - currBuiltinOp = new DataGenOp(OpOpDG.SEQ, target, randParams); - break; - - case TIME: - currBuiltinOp = new DataGenOp(OpOpDG.TIME, target); - break; - - case SAMPLE: { - Expression[] in = source.getAllExpr(); - - // arguments: range/size/replace/seed; defaults: replace=FALSE - - HashMap tmpparams = new HashMap<>(); - tmpparams.put(DataExpression.RAND_MAX, expr); // range - tmpparams.put(DataExpression.RAND_ROWS, expr2); - tmpparams.put(DataExpression.RAND_COLS, new LiteralOp(1)); - - if(in.length == 4) { + break; + + case MOMENT: + case COV: + case QUANTILE: + case INTERQUANTILE: + currBuiltinOp = (expr3 == null) ? new BinaryOp(target.getName(), target.getDataType(), target.getValueType(), + OpOp2.valueOf(source.getOpCode().name()), expr, expr2) : new TernaryOp(target.getName(), target.getDataType(), + target.getValueType(), OpOp3.valueOf(source.getOpCode().name()), expr, expr2,expr3); + break; + + case IQM: + case MEDIAN: + currBuiltinOp = (expr2 == null) ? new UnaryOp(target.getName(), target.getDataType(), target.getValueType(), + OpOp1.valueOf(source.getOpCode().name()), expr) : new BinaryOp(target.getName(), target.getDataType(), + target.getValueType(), OpOp2.valueOf(source.getOpCode().name()), expr, expr2); + break; + + case IFELSE: + currBuiltinOp=new TernaryOp(target.getName(), target.getDataType(), + target.getValueType(), OpOp3.IFELSE, expr, expr2, expr3); + break; + + case SEQ: + HashMap randParams = new HashMap<>(); + randParams.put(Statement.SEQ_FROM, expr); + randParams.put(Statement.SEQ_TO, expr2); + randParams.put(Statement.SEQ_INCR, (expr3!=null)?expr3 : new LiteralOp(1)); + //note incr: default -1 (for from>to) handled during runtime + currBuiltinOp = new DataGenOp(OpOpDG.SEQ, target, randParams); + break; + + case TIME: + currBuiltinOp = new DataGenOp(OpOpDG.TIME, target); + break; + + case SAMPLE: + { + Expression[] in = source.getAllExpr(); + + // arguments: range/size/replace/seed; defaults: replace=FALSE + + HashMap tmpparams = new HashMap<>(); + tmpparams.put(DataExpression.RAND_MAX, expr); //range + tmpparams.put(DataExpression.RAND_ROWS, expr2); + tmpparams.put(DataExpression.RAND_COLS, new LiteralOp(1)); + + if ( in.length == 4 ) + { + tmpparams.put(DataExpression.RAND_PDF, expr3); + Hop seed = processExpression(in[3], null, hops); + tmpparams.put(DataExpression.RAND_SEED, seed); + } + else if ( in.length == 3 ) + { + // check if the third argument is "replace" or "seed" + if ( expr3.getValueType() == ValueType.BOOLEAN ) + { tmpparams.put(DataExpression.RAND_PDF, expr3); - Hop seed = processExpression(in[3], null, hops); - tmpparams.put(DataExpression.RAND_SEED, seed); + tmpparams.put(DataExpression.RAND_SEED, new LiteralOp(DataGenOp.UNSPECIFIED_SEED) ); } - else if(in.length == 3) { - // check if the third argument is "replace" or "seed" - if(expr3.getValueType() == ValueType.BOOLEAN) { - tmpparams.put(DataExpression.RAND_PDF, expr3); - tmpparams.put(DataExpression.RAND_SEED, new LiteralOp(DataGenOp.UNSPECIFIED_SEED)); - } - else if(expr3.getValueType() == ValueType.INT64) { - tmpparams.put(DataExpression.RAND_PDF, new LiteralOp(false)); - tmpparams.put(DataExpression.RAND_SEED, expr3); - } - else - throw new HopsException("Invalid input type " + expr3.getValueType() + " in sample()."); - - } - else if(in.length == 2) { + else if ( expr3.getValueType() == ValueType.INT64 ) + { tmpparams.put(DataExpression.RAND_PDF, new LiteralOp(false)); - tmpparams.put(DataExpression.RAND_SEED, new LiteralOp(DataGenOp.UNSPECIFIED_SEED)); + tmpparams.put(DataExpression.RAND_SEED, expr3 ); } - - currBuiltinOp = new DataGenOp(OpOpDG.SAMPLE, target, tmpparams); - break; - } - - case SOLVE: - currBuiltinOp = new BinaryOp(target.getName(), target.getDataType(), target.getValueType(), OpOp2.SOLVE, - expr, expr2); - break; - - case INVERSE: - case CHOLESKY: - case TYPEOF: - case DETECTSCHEMA: - case COLNAMES: - currBuiltinOp = new UnaryOp(target.getName(), target.getDataType(), target.getValueType(), - OpOp1.valueOf(source.getOpCode().name()), expr); - break; - - case OUTER: - if(!(expr3 instanceof LiteralOp)) - throw new HopsException("Operator for outer builtin function must be a constant: " + expr3); - OpOp2 op = OpOp2.valueOfByOpcode(((LiteralOp) expr3).getStringValue()); - if(op == null) - throw new HopsException( - "Unsupported outer vector binary operation: " + ((LiteralOp) expr3).getStringValue()); - - currBuiltinOp = new BinaryOp(target.getName(), DataType.MATRIX, target.getValueType(), op, expr, expr2); - ((BinaryOp) currBuiltinOp).setOuterVectorOperation(true); // flag op as specific outer vector operation - currBuiltinOp.refreshSizeInformation(); // force size reevaluation according to 'outer' flag otherwise - // danger of incorrect dims - break; - - case BIASADD: - case BIASMULT: { - ArrayList inHops1 = new ArrayList<>(); - inHops1.add(expr); - inHops1.add(expr2); - currBuiltinOp = new DnnOp(target.getName(), DataType.MATRIX, target.getValueType(), - OpOpDnn.valueOf(source.getOpCode().name()), inHops1); - setBlockSizeAndRefreshSizeInfo(expr, currBuiltinOp); - break; - } - case AVG_POOL: - case MAX_POOL: { - currBuiltinOp = new DnnOp(target.getName(), DataType.MATRIX, target.getValueType(), - OpOpDnn.valueOf(source.getOpCode().name()), - getALHopsForPoolingForwardIM2COL(expr, source, 1, hops)); - setBlockSizeAndRefreshSizeInfo(expr, currBuiltinOp); - break; - } - case AVG_POOL_BACKWARD: - case MAX_POOL_BACKWARD: { - currBuiltinOp = new DnnOp(target.getName(), DataType.MATRIX, target.getValueType(), - OpOpDnn.valueOf(source.getOpCode().name()), getALHopsForConvOpPoolingCOL2IM(expr, source, 1, hops)); - setBlockSizeAndRefreshSizeInfo(expr, currBuiltinOp); - break; - } - case CONV2D: - case CONV2D_BACKWARD_FILTER: - case CONV2D_BACKWARD_DATA: { - currBuiltinOp = new DnnOp(target.getName(), DataType.MATRIX, target.getValueType(), - OpOpDnn.valueOf(source.getOpCode().name()), getALHopsForConvOp(expr, source, 1, hops)); - setBlockSizeAndRefreshSizeInfo(expr, currBuiltinOp); - break; - } - - case ROW_COUNT_DISTINCT: - currBuiltinOp = new AggUnaryOp(target.getName(), DataType.MATRIX, target.getValueType(), + else + throw new HopsException("Invalid input type " + expr3.getValueType() + " in sample()."); + + } + else if ( in.length == 2 ) + { + tmpparams.put(DataExpression.RAND_PDF, new LiteralOp(false)); + tmpparams.put(DataExpression.RAND_SEED, new LiteralOp(DataGenOp.UNSPECIFIED_SEED) ); + } + + currBuiltinOp = new DataGenOp(OpOpDG.SAMPLE, target, tmpparams); + break; + } + + case SOLVE: + currBuiltinOp = new BinaryOp(target.getName(), target.getDataType(), target.getValueType(), OpOp2.SOLVE, expr, expr2); + break; + + case INVERSE: + case CHOLESKY: + case TYPEOF: + case DETECTSCHEMA: + case COLNAMES: + currBuiltinOp = new UnaryOp(target.getName(), target.getDataType(), + target.getValueType(), OpOp1.valueOf(source.getOpCode().name()), expr); + break; + + case OUTER: + if( !(expr3 instanceof LiteralOp) ) + throw new HopsException("Operator for outer builtin function must be a constant: "+expr3); + OpOp2 op = OpOp2.valueOfByOpcode(((LiteralOp)expr3).getStringValue()); + if( op == null ) + throw new HopsException("Unsupported outer vector binary operation: "+((LiteralOp)expr3).getStringValue()); + + currBuiltinOp = new BinaryOp(target.getName(), DataType.MATRIX, target.getValueType(), op, expr, expr2); + ((BinaryOp)currBuiltinOp).setOuterVectorOperation(true); //flag op as specific outer vector operation + currBuiltinOp.refreshSizeInformation(); //force size reevaluation according to 'outer' flag otherwise danger of incorrect dims + break; + + case BIASADD: + case BIASMULT: { + ArrayList inHops1 = new ArrayList<>(); + inHops1.add(expr); + inHops1.add(expr2); + currBuiltinOp = new DnnOp(target.getName(), DataType.MATRIX, target.getValueType(), + OpOpDnn.valueOf(source.getOpCode().name()), inHops1); + setBlockSizeAndRefreshSizeInfo(expr, currBuiltinOp); + break; + } + case AVG_POOL: + case MAX_POOL: { + currBuiltinOp = new DnnOp(target.getName(), DataType.MATRIX, target.getValueType(), + OpOpDnn.valueOf(source.getOpCode().name()), getALHopsForPoolingForwardIM2COL(expr, source, 1, hops)); + setBlockSizeAndRefreshSizeInfo(expr, currBuiltinOp); + break; + } + case AVG_POOL_BACKWARD: + case MAX_POOL_BACKWARD: { + currBuiltinOp = new DnnOp(target.getName(), DataType.MATRIX, target.getValueType(), + OpOpDnn.valueOf(source.getOpCode().name()), getALHopsForConvOpPoolingCOL2IM(expr, source, 1, hops)); + setBlockSizeAndRefreshSizeInfo(expr, currBuiltinOp); + break; + } + case CONV2D: + case CONV2D_BACKWARD_FILTER: + case CONV2D_BACKWARD_DATA: { + currBuiltinOp = new DnnOp(target.getName(), DataType.MATRIX, target.getValueType(), + OpOpDnn.valueOf(source.getOpCode().name()), getALHopsForConvOp(expr, source, 1, hops)); + setBlockSizeAndRefreshSizeInfo(expr, currBuiltinOp); + break; + } + + case ROW_COUNT_DISTINCT: + currBuiltinOp = new AggUnaryOp(target.getName(), DataType.MATRIX, target.getValueType(), AggOp.valueOf(source.getOpCode().name()), Direction.Row, expr); - break; + break; - case COL_COUNT_DISTINCT: - currBuiltinOp = new AggUnaryOp(target.getName(), DataType.MATRIX, target.getValueType(), + case COL_COUNT_DISTINCT: + currBuiltinOp = new AggUnaryOp(target.getName(), DataType.MATRIX, target.getValueType(), AggOp.valueOf(source.getOpCode().name()), Direction.Col, expr); - break; + break; - default: - throw new ParseException("Unsupported builtin function type: " + source.getOpCode()); + default: + throw new ParseException("Unsupported builtin function type: "+source.getOpCode()); } - boolean isConvolution = source.getOpCode() == Builtins.CONV2D || - source.getOpCode() == Builtins.CONV2D_BACKWARD_DATA || - source.getOpCode() == Builtins.CONV2D_BACKWARD_FILTER || source.getOpCode() == Builtins.MAX_POOL || - source.getOpCode() == Builtins.MAX_POOL_BACKWARD || source.getOpCode() == Builtins.AVG_POOL || - source.getOpCode() == Builtins.AVG_POOL_BACKWARD; - if(!isConvolution) { + boolean isConvolution = source.getOpCode() == Builtins.CONV2D || source.getOpCode() == Builtins.CONV2D_BACKWARD_DATA || + source.getOpCode() == Builtins.CONV2D_BACKWARD_FILTER || + source.getOpCode() == Builtins.MAX_POOL || source.getOpCode() == Builtins.MAX_POOL_BACKWARD || + source.getOpCode() == Builtins.AVG_POOL || source.getOpCode() == Builtins.AVG_POOL_BACKWARD; + if( !isConvolution) { // Since the dimension of output doesnot match that of input variable for these operations setIdentifierParams(currBuiltinOp, source.getOutput()); } @@ -2872,7 +2819,7 @@ else if(in.length == 2) { private Hop[] processAllExpressions(Expression[] expr, HashMap hops) { Hop[] ret = new Hop[expr.length]; - for(int i = 0; i < expr.length; i++) + for(int i=0; i getALHopsForConvOpPoolingCOL2IM(Hop first, BuiltinFunctionExpression source, int skip, - HashMap hops) { + private ArrayList getALHopsForConvOpPoolingCOL2IM(Hop first, BuiltinFunctionExpression source, int skip, HashMap hops) { ArrayList ret = new ArrayList<>(); ret.add(first); Expression[] allExpr = source.getAllExpr(); for(int i = skip; i < allExpr.length; i++) { if(i == 11) { - ret.add(processExpression(allExpr[7], null, hops)); // Make number of channels of images and filter the - // same + ret.add(processExpression(allExpr[7], null, hops)); // Make number of channels of images and filter the same } else ret.add(processExpression(allExpr[i], null, hops)); @@ -2900,8 +2845,7 @@ private ArrayList getALHopsForConvOpPoolingCOL2IM(Hop first, BuiltinFunctio return ret; } - private ArrayList getALHopsForPoolingForwardIM2COL(Hop first, BuiltinFunctionExpression source, int skip, - HashMap hops) { + private ArrayList getALHopsForPoolingForwardIM2COL(Hop first, BuiltinFunctionExpression source, int skip, HashMap hops) { ArrayList ret = new ArrayList<>(); ret.add(first); Expression[] allExpr = source.getAllExpr(); @@ -2912,7 +2856,7 @@ private ArrayList getALHopsForPoolingForwardIM2COL(Hop first, BuiltinFuncti Expression numChannels = allExpr[6]; for(int i = skip; i < allExpr.length; i++) { - if(i == 10) { + if(i == 10) { ret.add(processExpression(numChannels, null, hops)); } else @@ -2921,9 +2865,8 @@ private ArrayList getALHopsForPoolingForwardIM2COL(Hop first, BuiltinFuncti return ret; } - @SuppressWarnings("unused") // TODO remove if not used - private ArrayList getALHopsForConvOpPoolingIM2COL(Hop first, BuiltinFunctionExpression source, int skip, - HashMap hops) { + @SuppressWarnings("unused") //TODO remove if not used + private ArrayList getALHopsForConvOpPoolingIM2COL(Hop first, BuiltinFunctionExpression source, int skip, HashMap hops) { ArrayList ret = new ArrayList(); ret.add(first); Expression[] allExpr = source.getAllExpr(); @@ -2938,8 +2881,8 @@ else if(skip == 2) { throw new ParseException("Unsupported skip"); } - for(int i = skip; i < allExpr.length; i++) { - if(i == numImgIndex) { // skip=1 ==> i==5 and skip=2 => i==6 + for (int i = skip; i < allExpr.length; i++) { + if (i == numImgIndex) { // skip=1 ==> i==5 and skip=2 => i==6 Expression numImg = allExpr[numImgIndex]; Expression numChannels = allExpr[numImgIndex + 1]; BinaryExpression tmp = new BinaryExpression(org.apache.sysds.parser.Expression.BinaryOp.MULT, numImg); @@ -2948,15 +2891,13 @@ else if(skip == 2) { ret.add(processTempIntExpression(tmp, hops)); ret.add(processExpression(new IntIdentifier(1, numImg), null, hops)); i++; - } - else + } else ret.add(processExpression(allExpr[i], null, hops)); } return ret; } - private ArrayList getALHopsForConvOp(Hop first, BuiltinFunctionExpression source, int skip, - HashMap hops) { + private ArrayList getALHopsForConvOp(Hop first, BuiltinFunctionExpression source, int skip, HashMap hops) { ArrayList ret = new ArrayList<>(); ret.add(first); Expression[] allExpr = source.getAllExpr(); @@ -2967,94 +2908,101 @@ private ArrayList getALHopsForConvOp(Hop first, BuiltinFunctionExpression s } public void setIdentifierParams(Hop h, Identifier id) { - if(id.getDim1() >= 0) + if( id.getDim1()>= 0 ) h.setDim1(id.getDim1()); - if(id.getDim2() >= 0) + if( id.getDim2()>= 0 ) h.setDim2(id.getDim2()); - if(id.getNnz() >= 0) + if( id.getNnz()>= 0 ) h.setNnz(id.getNnz()); h.setBlocksize(id.getBlocksize()); h.setPrivacy(id.getPrivacy()); } - private boolean prepareReadAfterWrite(DMLProgram prog, HashMap pWrites) { + private boolean prepareReadAfterWrite( DMLProgram prog, HashMap pWrites ) { boolean ret = false; - // process functions - /* - * MB: for the moment we only support read-after-write in the main program for( FunctionStatementBlock fsb : - * prog.getFunctionStatementBlocks() ) ret |= prepareReadAfterWrite(fsb, pWrites); - */ + //process functions + /*MB: for the moment we only support read-after-write in the main program + for( FunctionStatementBlock fsb : prog.getFunctionStatementBlocks() ) + ret |= prepareReadAfterWrite(fsb, pWrites); + */ - // process main program - for(StatementBlock sb : prog.getStatementBlocks()) + //process main program + for( StatementBlock sb : prog.getStatementBlocks() ) ret |= prepareReadAfterWrite(sb, pWrites); return ret; } - private boolean prepareReadAfterWrite(StatementBlock sb, HashMap pWrites) { + private boolean prepareReadAfterWrite( StatementBlock sb, HashMap pWrites ) + { boolean ret = false; - if(sb instanceof FunctionStatementBlock) { + if(sb instanceof FunctionStatementBlock) + { FunctionStatementBlock fsb = (FunctionStatementBlock) sb; - FunctionStatement fstmt = (FunctionStatement) fsb.getStatement(0); - for(StatementBlock csb : fstmt.getBody()) + FunctionStatement fstmt = (FunctionStatement)fsb.getStatement(0); + for (StatementBlock csb : fstmt.getBody()) ret |= prepareReadAfterWrite(csb, pWrites); } - else if(sb instanceof WhileStatementBlock) { + else if(sb instanceof WhileStatementBlock) + { WhileStatementBlock wsb = (WhileStatementBlock) sb; - WhileStatement wstmt = (WhileStatement) wsb.getStatement(0); - for(StatementBlock csb : wstmt.getBody()) + WhileStatement wstmt = (WhileStatement)wsb.getStatement(0); + for (StatementBlock csb : wstmt.getBody()) ret |= prepareReadAfterWrite(csb, pWrites); } - else if(sb instanceof IfStatementBlock) { + else if(sb instanceof IfStatementBlock) + { IfStatementBlock isb = (IfStatementBlock) sb; - IfStatement istmt = (IfStatement) isb.getStatement(0); - for(StatementBlock csb : istmt.getIfBody()) + IfStatement istmt = (IfStatement)isb.getStatement(0); + for (StatementBlock csb : istmt.getIfBody()) ret |= prepareReadAfterWrite(csb, pWrites); - for(StatementBlock csb : istmt.getElseBody()) + for (StatementBlock csb : istmt.getElseBody()) ret |= prepareReadAfterWrite(csb, pWrites); } - else if(sb instanceof ForStatementBlock) // incl parfor + else if(sb instanceof ForStatementBlock) //incl parfor { ForStatementBlock fsb = (ForStatementBlock) sb; - ForStatement fstmt = (ForStatement) fsb.getStatement(0); - for(StatementBlock csb : fstmt.getBody()) + ForStatement fstmt = (ForStatement)fsb.getStatement(0); + for (StatementBlock csb : fstmt.getBody()) ret |= prepareReadAfterWrite(csb, pWrites); } - else // generic (last-level) + else //generic (last-level) { - for(Statement s : sb.getStatements()) { - // collect persistent write information - if(s instanceof OutputStatement) { + for( Statement s : sb.getStatements() ) + { + //collect persistent write information + if( s instanceof OutputStatement ) + { OutputStatement os = (OutputStatement) s; String pfname = os.getExprParam(DataExpression.IO_FILENAME).toString(); DataIdentifier di = (DataIdentifier) os.getSource().getOutput(); pWrites.put(pfname, di); } - // propagate size information into reads-after-write - else if(s instanceof AssignmentStatement && - ((AssignmentStatement) s).getSource() instanceof DataExpression) { - DataExpression dexpr = (DataExpression) ((AssignmentStatement) s).getSource(); - if(dexpr.isRead()) { + //propagate size information into reads-after-write + else if( s instanceof AssignmentStatement + && ((AssignmentStatement)s).getSource() instanceof DataExpression ) + { + DataExpression dexpr = (DataExpression) ((AssignmentStatement)s).getSource(); + if (dexpr.isRead()) { String pfname = dexpr.getVarParam(DataExpression.IO_FILENAME).toString(); // found read-after-write - if(pWrites.containsKey(pfname) && !pfname.trim().isEmpty()) { + if (pWrites.containsKey(pfname) && !pfname.trim().isEmpty()) { // update read with essential write meta data DataIdentifier di = pWrites.get(pfname); FileFormat ft = (di.getFileFormat() != null) ? di.getFileFormat() : FileFormat.TEXT; dexpr.addVarParam(DataExpression.FORMAT_TYPE, new StringIdentifier(ft.toString(), di)); - if(di.getDim1() >= 0) + if (di.getDim1() >= 0) dexpr.addVarParam(DataExpression.READROWPARAM, new IntIdentifier(di.getDim1(), di)); - if(di.getDim2() >= 0) + if (di.getDim2() >= 0) dexpr.addVarParam(DataExpression.READCOLPARAM, new IntIdentifier(di.getDim2(), di)); - if(di.getValueType() != ValueType.UNKNOWN) + if (di.getValueType() != ValueType.UNKNOWN) dexpr.addVarParam(DataExpression.VALUETYPEPARAM, - new StringIdentifier(di.getValueType().toExternalString(), di)); - if(di.getDataType() != DataType.UNKNOWN) + new StringIdentifier(di.getValueType().toExternalString(), di)); + if (di.getDataType() != DataType.UNKNOWN) dexpr.addVarParam(DataExpression.DATATYPEPARAM, - new StringIdentifier(di.getDataType().toString(), di)); + new StringIdentifier(di.getDataType().toString(), di)); ret = true; } } diff --git a/src/main/java/org/apache/sysds/runtime/instructions/CPInstructionParser.java b/src/main/java/org/apache/sysds/runtime/instructions/CPInstructionParser.java index 829cbcf21d3..a8702a39b1b 100644 --- a/src/main/java/org/apache/sysds/runtime/instructions/CPInstructionParser.java +++ b/src/main/java/org/apache/sysds/runtime/instructions/CPInstructionParser.java @@ -350,22 +350,22 @@ public class CPInstructionParser extends InstructionParser { } public static CPInstruction parseSingleInstruction(String str) { - if(str == null || str.isEmpty()) + if (str == null || str.isEmpty()) return null; CPType cptype = InstructionUtils.getCPType(str); - if(cptype == null) + if (cptype == null) throw new DMLRuntimeException("Unable derive cptype for instruction: " + str); CPInstruction cpinst = parseSingleInstruction(cptype, str); - if(cpinst == null) + if (cpinst == null) throw new DMLRuntimeException("Unable to parse instruction: " + str); return cpinst; } public static CPInstruction parseSingleInstruction(CPType cptype, String str) { ExecType execType; - if(str == null || str.isEmpty()) + if (str == null || str.isEmpty()) return null; - switch(cptype) { + switch (cptype) { case AggregateUnary: return AggregateUnaryCPInstruction.parseInstruction(str); @@ -440,15 +440,15 @@ public static CPInstruction parseSingleInstruction(CPType cptype, String str) { case MatrixIndexing: execType = ExecType.valueOf(str.split(Instruction.OPERAND_DELIM)[0]); - if(execType == ExecType.CP) + if (execType == ExecType.CP) return IndexingCPInstruction.parseInstruction(str); else // exectype CP_FILE return MatrixIndexingCPFileInstruction.parseInstruction(str); case Builtin: String[] parts = InstructionUtils.getInstructionPartsWithValueType(str); - if(parts[0].equals("log") || parts[0].equals("log_nz")) { - if(InstructionUtils.isInteger(parts[3])) // B=log(A), y=log(x) + if (parts[0].equals("log") || parts[0].equals("log_nz")) { + if (InstructionUtils.isInteger(parts[3])) // B=log(A), y=log(x) // We exploit the fact the number of threads is specified as an integer at parts // 3. return UnaryCPInstruction.parseInstruction(str); diff --git a/src/main/java/org/apache/sysds/runtime/instructions/cp/CPInstruction.java b/src/main/java/org/apache/sysds/runtime/instructions/cp/CPInstruction.java index 17c549a863b..23627060d04 100644 --- a/src/main/java/org/apache/sysds/runtime/instructions/cp/CPInstruction.java +++ b/src/main/java/org/apache/sysds/runtime/instructions/cp/CPInstruction.java @@ -92,18 +92,18 @@ public Instruction preprocessInstruction(ExecutionContext ec) { Instruction tmp = super.preprocessInstruction(ec); // instruction patching - if(tmp.requiresLabelUpdate()) { // update labels only if required + if (tmp.requiresLabelUpdate()) { // update labels only if required // note: no exchange of updated instruction as labels might change in the // general case String updInst = updateLabels(tmp.toString(), ec.getVariables()); tmp = CPInstructionParser.parseSingleInstruction(updInst); // Corrected lineage trace for patched instructions - if(DMLScript.LINEAGE) + if (DMLScript.LINEAGE) ec.traceLineage(tmp); } // robustness federated instructions (runtime assignment) - if(ConfigurationManager.isFederatedRuntimePlanner()) { + if (ConfigurationManager.isFederatedRuntimePlanner()) { tmp = FEDInstructionUtils.checkAndReplaceCP(tmp, ec); // NOTE: Retracing of lineage is not needed as the lineage trace // is same for an instruction and its FED version. @@ -118,28 +118,28 @@ public Instruction preprocessInstruction(ExecutionContext ec) { @Override public void postprocessInstruction(ExecutionContext ec) { - if(DMLScript.LINEAGE_DEBUGGER) + if (DMLScript.LINEAGE_DEBUGGER) ec.maintainLineageDebuggerInfo(this); } /** - * Takes a delimited string of instructions, and replaces ALL placeholder labels (such as ##mVar2## and ##Var5##) in - * ALL instructions. - * + * Takes a delimited string of instructions, and replaces ALL placeholder labels + * (such as ##mVar2## and ##Var5##) in ALL instructions. + * * @param instList instruction list as string * @param labelValueMapping local variable map * @return instruction list after replacement */ public static String updateLabels(String instList, LocalVariableMap labelValueMapping) { - if(!instList.contains(Lop.VARIABLE_NAME_PLACEHOLDER)) + if (!instList.contains(Lop.VARIABLE_NAME_PLACEHOLDER)) return instList; StringBuilder updateInstList = new StringBuilder(); String[] ilist = instList.split(Lop.INSTRUCTION_DELIMITOR); - for(int i = 0; i < ilist.length; i++) { - if(i > 0) + for (int i = 0; i < ilist.length; i++) { + if (i > 0) updateInstList.append(Lop.INSTRUCTION_DELIMITOR); updateInstList.append(updateInstLabels(ilist[i], labelValueMapping)); @@ -148,31 +148,33 @@ public static String updateLabels(String instList, LocalVariableMap labelValueMa } /** - * Replaces ALL placeholder strings (such as ##mVar2## and ##Var5##) in a single instruction. - * + * Replaces ALL placeholder strings (such as ##mVar2## and ##Var5##) in a single + * instruction. + * * @param inst string instruction * @param map local variable map * @return string instruction after replacement */ private static String updateInstLabels(String inst, LocalVariableMap map) { - if(inst.contains(Lop.VARIABLE_NAME_PLACEHOLDER)) { + if (inst.contains(Lop.VARIABLE_NAME_PLACEHOLDER)) { int skip = Lop.VARIABLE_NAME_PLACEHOLDER.length(); - while(inst.contains(Lop.VARIABLE_NAME_PLACEHOLDER)) { + while (inst.contains(Lop.VARIABLE_NAME_PLACEHOLDER)) { int startLoc = inst.indexOf(Lop.VARIABLE_NAME_PLACEHOLDER) + skip; String varName = inst.substring(startLoc, inst.indexOf(Lop.VARIABLE_NAME_PLACEHOLDER, startLoc)); String replacement = getVarNameReplacement(inst, varName, map); inst = inst.replaceAll(Lop.VARIABLE_NAME_PLACEHOLDER + varName + Lop.VARIABLE_NAME_PLACEHOLDER, - replacement); + replacement); } } return inst; } /** - * Computes the replacement string for a given variable name placeholder string (e.g., ##mVar2## or ##Var5##). The - * replacement is a HDFS filename for matrix variables, and is the actual value (stored in symbol table) for scalar + * Computes the replacement string for a given variable name placeholder string + * (e.g., ##mVar2## or ##Var5##). The replacement is a HDFS filename for matrix + * variables, and is the actual value (stored in symbol table) for scalar * variables. - * + * * @param inst instruction * @param varName variable name * @param map local variable map @@ -180,19 +182,18 @@ private static String updateInstLabels(String inst, LocalVariableMap map) { */ private static String getVarNameReplacement(String inst, String varName, LocalVariableMap map) { Data val = map.get(varName); - if(val != null) { + if (val != null) { String replacement = null; - if(val.getDataType() == DataType.MATRIX) { + if (val.getDataType() == DataType.MATRIX) { replacement = ((MatrixObject) val).getFileName(); } - if(val.getDataType() == DataType.SCALAR) + if (val.getDataType() == DataType.SCALAR) replacement = "" + ((ScalarObject) val).getStringValue(); return replacement; - } - else { + } else { throw new DMLRuntimeException( - "Variable (" + varName + ") in Instruction (" + inst + ") is not found in the variablemap."); + "Variable (" + varName + ") in Instruction (" + inst + ") is not found in the variablemap."); } } } diff --git a/src/main/java/org/apache/sysds/runtime/instructions/cp/MultiReturnBuiltinCPInstruction.java b/src/main/java/org/apache/sysds/runtime/instructions/cp/MultiReturnBuiltinCPInstruction.java index 051a6928c99..26874cc8708 100644 --- a/src/main/java/org/apache/sysds/runtime/instructions/cp/MultiReturnBuiltinCPInstruction.java +++ b/src/main/java/org/apache/sysds/runtime/instructions/cp/MultiReturnBuiltinCPInstruction.java @@ -39,7 +39,7 @@ public class MultiReturnBuiltinCPInstruction extends ComputationCPInstruction { protected ArrayList _outputs; private MultiReturnBuiltinCPInstruction(Operator op, CPOperand input1, ArrayList outputs, String opcode, - String istr) { + String istr) { super(CPType.MultiReturnBuiltin, op, input1, null, outputs.get(0), opcode, istr); _outputs = outputs; } @@ -62,15 +62,14 @@ public static MultiReturnBuiltinCPInstruction parseInstruction(String str) { // first part is always the opcode String opcode = parts[0]; - if(opcode.equalsIgnoreCase("qr")) { + if (opcode.equalsIgnoreCase("qr")) { // one input and two ouputs CPOperand in1 = new CPOperand(parts[1]); outputs.add(new CPOperand(parts[2], ValueType.FP64, DataType.MATRIX)); outputs.add(new CPOperand(parts[3], ValueType.FP64, DataType.MATRIX)); return new MultiReturnBuiltinCPInstruction(null, in1, outputs, opcode, str); - } - else if(opcode.equalsIgnoreCase("lu")) { + } else if (opcode.equalsIgnoreCase("lu")) { CPOperand in1 = new CPOperand(parts[1]); // one input and three outputs @@ -80,8 +79,7 @@ else if(opcode.equalsIgnoreCase("lu")) { return new MultiReturnBuiltinCPInstruction(null, in1, outputs, opcode, str); - } - else if(opcode.equalsIgnoreCase("eigen")) { + } else if (opcode.equalsIgnoreCase("eigen")) { // one input and two outputs CPOperand in1 = new CPOperand(parts[1]); outputs.add(new CPOperand(parts[2], ValueType.FP64, DataType.MATRIX)); @@ -89,8 +87,7 @@ else if(opcode.equalsIgnoreCase("eigen")) { return new MultiReturnBuiltinCPInstruction(null, in1, outputs, opcode, str); - } - else if(opcode.equalsIgnoreCase("fft")) { + } else if (opcode.equalsIgnoreCase("fft")) { // one input and two outputs CPOperand in1 = new CPOperand(parts[1]); outputs.add(new CPOperand(parts[2], ValueType.FP64, DataType.MATRIX)); @@ -98,8 +95,7 @@ else if(opcode.equalsIgnoreCase("fft")) { return new MultiReturnBuiltinCPInstruction(null, in1, outputs, opcode, str); - } - else if(opcode.equalsIgnoreCase("svd")) { + } else if (opcode.equalsIgnoreCase("svd")) { CPOperand in1 = new CPOperand(parts[1]); // one input and three outputs @@ -109,8 +105,7 @@ else if(opcode.equalsIgnoreCase("svd")) { return new MultiReturnBuiltinCPInstruction(null, in1, outputs, opcode, str); - } - else { + } else { throw new DMLRuntimeException("Invalid opcode in MultiReturnBuiltin instruction: " + opcode); } @@ -122,13 +117,13 @@ public int getNumOutputs() { @Override public void processInstruction(ExecutionContext ec) { - if(!LibCommonsMath.isSupportedMultiReturnOperation(getOpcode())) + if (!LibCommonsMath.isSupportedMultiReturnOperation(getOpcode())) throw new DMLRuntimeException("Invalid opcode in MultiReturnBuiltin instruction: " + getOpcode()); MatrixBlock in = ec.getMatrixInput(input1.getName()); MatrixBlock[] out = LibCommonsMath.multiReturnOperations(in, getOpcode()); ec.releaseMatrixInput(input1.getName()); - for(int i = 0; i < _outputs.size(); i++) { + for (int i = 0; i < _outputs.size(); i++) { ec.setMatrixOutput(_outputs.get(i).getName(), out[i]); } } @@ -143,7 +138,7 @@ public boolean hasSingleLineage() { public Pair[] getLineageItems(ExecutionContext ec) { LineageItem[] inputLineage = LineageItemUtils.getLineage(ec, input1, input2, input3); final Pair[] ret = new Pair[_outputs.size()]; - for(int i = 0; i < _outputs.size(); i++) { + for (int i = 0; i < _outputs.size(); i++) { CPOperand out = _outputs.get(i); ret[i] = Pair.of(out.getName(), new LineageItem(getOpcode(), inputLineage)); } diff --git a/src/main/java/org/apache/sysds/runtime/instructions/cp/MultiReturnComplexMatrixBuiltinCPInstruction.java b/src/main/java/org/apache/sysds/runtime/instructions/cp/MultiReturnComplexMatrixBuiltinCPInstruction.java index 66f9f307455..ae045dbc3e5 100644 --- a/src/main/java/org/apache/sysds/runtime/instructions/cp/MultiReturnComplexMatrixBuiltinCPInstruction.java +++ b/src/main/java/org/apache/sysds/runtime/instructions/cp/MultiReturnComplexMatrixBuiltinCPInstruction.java @@ -36,116 +36,117 @@ public class MultiReturnComplexMatrixBuiltinCPInstruction extends ComputationCPInstruction { - protected ArrayList _outputs; - - private MultiReturnComplexMatrixBuiltinCPInstruction(Operator op, CPOperand input1, CPOperand input2, - ArrayList outputs, String opcode, String istr) { - super(CPType.MultiReturnBuiltin, op, input1, input2, outputs.get(0), opcode, istr); - _outputs = outputs; - } - - private MultiReturnComplexMatrixBuiltinCPInstruction(Operator op, CPOperand input1, ArrayList outputs, - String opcode, String istr) { - super(CPType.MultiReturnBuiltin, op, input1, null, outputs.get(0), opcode, istr); - _outputs = outputs; - } - - public CPOperand getOutput(int i) { - return _outputs.get(i); - } - - public List getOutputs() { - return _outputs; - } - - public String[] getOutputNames() { - return _outputs.parallelStream().map(output -> output.getName()).toArray(String[]::new); - } - - public static MultiReturnComplexMatrixBuiltinCPInstruction parseInstruction(String str) { - String[] parts = InstructionUtils.getInstructionPartsWithValueType(str); - ArrayList outputs = new ArrayList<>(); - // first part is always the opcode - String opcode = parts[0]; - - if(parts.length == 5 && opcode.equalsIgnoreCase("ifft")) { - // one input and two outputs - CPOperand in1 = new CPOperand(parts[1]); - CPOperand in2 = new CPOperand(parts[2]); - outputs.add(new CPOperand(parts[3], ValueType.FP64, DataType.MATRIX)); - outputs.add(new CPOperand(parts[4], ValueType.FP64, DataType.MATRIX)); - - return new MultiReturnComplexMatrixBuiltinCPInstruction(null, in1, in2, outputs, opcode, str); - } - else if(parts.length == 4 && opcode.equalsIgnoreCase("ifft")) { - // one input and two outputs - CPOperand in1 = new CPOperand(parts[1]); - outputs.add(new CPOperand(parts[2], ValueType.FP64, DataType.MATRIX)); - outputs.add(new CPOperand(parts[3], ValueType.FP64, DataType.MATRIX)); - - return new MultiReturnComplexMatrixBuiltinCPInstruction(null, in1, outputs, opcode, str); - } - - { - throw new DMLRuntimeException("Invalid opcode in MultiReturnBuiltin instruction: " + opcode); - } - - } - - public int getNumOutputs() { - return _outputs.size(); - } - - @Override - public void processInstruction(ExecutionContext ec) { - if(input2 == null) - processOneInputInstruction(ec); - else - processTwoInputInstruction(ec); - } - - private void processOneInputInstruction(ExecutionContext ec) { - if(!LibCommonsMath.isSupportedMultiReturnOperation(getOpcode())) - throw new DMLRuntimeException("Invalid opcode in MultiReturnBuiltin instruction: " + getOpcode()); - - MatrixBlock in = ec.getMatrixInput(input1.getName()); - MatrixBlock[] out = LibCommonsMath.multiReturnOperations(in, getOpcode()); - - ec.releaseMatrixInput(input1.getName()); - - for(int i = 0; i < _outputs.size(); i++) { - ec.setMatrixOutput(_outputs.get(i).getName(), out[i]); - } - } - - private void processTwoInputInstruction(ExecutionContext ec) { - if(!LibCommonsMath.isSupportedMultiReturnOperation(getOpcode())) - throw new DMLRuntimeException("Invalid opcode in MultiReturnBuiltin instruction: " + getOpcode()); - - MatrixBlock in1 = ec.getMatrixInput(input1.getName()); - MatrixBlock in2 = ec.getMatrixInput(input2.getName()); - MatrixBlock[] out = LibCommonsMath.multiReturnOperations(in1, in2, getOpcode()); - ec.releaseMatrixInput(input1.getName(), input2.getName()); - - for(int i = 0; i < _outputs.size(); i++) { - ec.setMatrixOutput(_outputs.get(i).getName(), out[i]); - } - } - - @Override - public boolean hasSingleLineage() { - return false; - } - - @Override - @SuppressWarnings("unchecked") - public Pair[] getLineageItems(ExecutionContext ec) { - LineageItem[] inputLineage = LineageItemUtils.getLineage(ec, input1, input2, input3); - final Pair[] ret = new Pair[_outputs.size()]; - for(int i = 0; i < _outputs.size(); i++) { - CPOperand out = _outputs.get(i); - ret[i] = Pair.of(out.getName(), new LineageItem(getOpcode(), inputLineage)); - } - return ret; - } + protected ArrayList _outputs; + + private MultiReturnComplexMatrixBuiltinCPInstruction(Operator op, CPOperand input1, CPOperand input2, + ArrayList outputs, String opcode, + String istr) { + super(CPType.MultiReturnBuiltin, op, input1, input2, outputs.get(0), opcode, istr); + _outputs = outputs; + } + + private MultiReturnComplexMatrixBuiltinCPInstruction(Operator op, CPOperand input1, ArrayList outputs, + String opcode, + String istr) { + super(CPType.MultiReturnBuiltin, op, input1, null, outputs.get(0), opcode, istr); + _outputs = outputs; + } + + public CPOperand getOutput(int i) { + return _outputs.get(i); + } + + public List getOutputs() { + return _outputs; + } + + public String[] getOutputNames() { + return _outputs.parallelStream().map(output -> output.getName()).toArray(String[]::new); + } + + public static MultiReturnComplexMatrixBuiltinCPInstruction parseInstruction(String str) { + String[] parts = InstructionUtils.getInstructionPartsWithValueType(str); + ArrayList outputs = new ArrayList<>(); + // first part is always the opcode + String opcode = parts[0]; + + if (parts.length == 5 && opcode.equalsIgnoreCase("ifft")) { + // one input and two outputs + CPOperand in1 = new CPOperand(parts[1]); + CPOperand in2 = new CPOperand(parts[2]); + outputs.add(new CPOperand(parts[3], ValueType.FP64, DataType.MATRIX)); + outputs.add(new CPOperand(parts[4], ValueType.FP64, DataType.MATRIX)); + + return new MultiReturnComplexMatrixBuiltinCPInstruction(null, in1, in2, outputs, opcode, str); + } else if (parts.length == 4 && opcode.equalsIgnoreCase("ifft")) { + // one input and two outputs + CPOperand in1 = new CPOperand(parts[1]); + outputs.add(new CPOperand(parts[2], ValueType.FP64, DataType.MATRIX)); + outputs.add(new CPOperand(parts[3], ValueType.FP64, DataType.MATRIX)); + + return new MultiReturnComplexMatrixBuiltinCPInstruction(null, in1, outputs, opcode, str); + } + + { + throw new DMLRuntimeException("Invalid opcode in MultiReturnBuiltin instruction: " + opcode); + } + + } + + public int getNumOutputs() { + return _outputs.size(); + } + + @Override + public void processInstruction(ExecutionContext ec) { + if (input2 == null) + processOneInputInstruction(ec); + else + processTwoInputInstruction(ec); + } + + private void processOneInputInstruction(ExecutionContext ec) { + if (!LibCommonsMath.isSupportedMultiReturnOperation(getOpcode())) + throw new DMLRuntimeException("Invalid opcode in MultiReturnBuiltin instruction: " + getOpcode()); + + MatrixBlock in = ec.getMatrixInput(input1.getName()); + MatrixBlock[] out = LibCommonsMath.multiReturnOperations(in, getOpcode()); + + ec.releaseMatrixInput(input1.getName()); + + for (int i = 0; i < _outputs.size(); i++) { + ec.setMatrixOutput(_outputs.get(i).getName(), out[i]); + } + } + + private void processTwoInputInstruction(ExecutionContext ec) { + if (!LibCommonsMath.isSupportedMultiReturnOperation(getOpcode())) + throw new DMLRuntimeException("Invalid opcode in MultiReturnBuiltin instruction: " + getOpcode()); + + MatrixBlock in1 = ec.getMatrixInput(input1.getName()); + MatrixBlock in2 = ec.getMatrixInput(input2.getName()); + MatrixBlock[] out = LibCommonsMath.multiReturnOperations(in1, in2, getOpcode()); + ec.releaseMatrixInput(input1.getName(), input2.getName()); + + for (int i = 0; i < _outputs.size(); i++) { + ec.setMatrixOutput(_outputs.get(i).getName(), out[i]); + } + } + + @Override + public boolean hasSingleLineage() { + return false; + } + + @Override + @SuppressWarnings("unchecked") + public Pair[] getLineageItems(ExecutionContext ec) { + LineageItem[] inputLineage = LineageItemUtils.getLineage(ec, input1, input2, input3); + final Pair[] ret = new Pair[_outputs.size()]; + for (int i = 0; i < _outputs.size(); i++) { + CPOperand out = _outputs.get(i); + ret[i] = Pair.of(out.getName(), new LineageItem(getOpcode(), inputLineage)); + } + return ret; + } } diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java index 2c3e514f9cf..924477100d7 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java @@ -53,9 +53,11 @@ import static org.apache.sysds.runtime.matrix.data.LibMatrixFourier.ifft; /** - * Library for matrix operations that need invocation of Apache Commons Math library. + * Library for matrix operations that need invocation of + * Apache Commons Math library. * - * This library currently supports following operations: matrix inverse, matrix decompositions (QR, LU, Eigen), solve + * This library currently supports following operations: + * matrix inverse, matrix decompositions (QR, LU, Eigen), solve */ public class LibCommonsMath { private static final Log LOG = LogFactory.getLog(LibCommonsMath.class.getName()); @@ -67,12 +69,12 @@ private LibCommonsMath() { } public static boolean isSupportedUnaryOperation(String opcode) { - return(opcode.equals("inverse") || opcode.equals("cholesky")); + return (opcode.equals("inverse") || opcode.equals("cholesky")); } public static boolean isSupportedMultiReturnOperation(String opcode) { - switch(opcode) { + switch (opcode) { case "qr": case "lu": case "eigen": @@ -87,14 +89,14 @@ public static boolean isSupportedMultiReturnOperation(String opcode) { } public static boolean isSupportedMatrixMatrixOperation(String opcode) { - return(opcode.equals("solve")); + return (opcode.equals("solve")); } public static MatrixBlock unaryOperations(MatrixBlock inj, String opcode) { Array2DRowRealMatrix matrixInput = DataConverter.convertToArray2DRowRealMatrix(inj); - if(opcode.equals("inverse")) + if (opcode.equals("inverse")) return computeMatrixInverse(matrixInput); - else if(opcode.equals("cholesky")) + else if (opcode.equals("cholesky")) return computeCholesky(matrixInput); return null; } @@ -108,8 +110,8 @@ public static MatrixBlock[] multiReturnOperations(MatrixBlock in1, MatrixBlock i } public static MatrixBlock[] multiReturnOperations(MatrixBlock in, String opcode, int threads, int num_iterations, - double tol) { - if(opcode.equals("eigen_qr")) + double tol) { + if (opcode.equals("eigen_qr")) return computeEigenQR(in, num_iterations, tol, threads); else return multiReturnOperations(in, opcode, threads, 1); @@ -117,7 +119,7 @@ public static MatrixBlock[] multiReturnOperations(MatrixBlock in, String opcode, public static MatrixBlock[] multiReturnOperations(MatrixBlock in, String opcode, int threads, long seed) { - switch(opcode) { + switch (opcode) { case "qr": return computeQR(in); case "qr2": @@ -143,9 +145,9 @@ public static MatrixBlock[] multiReturnOperations(MatrixBlock in, String opcode, } public static MatrixBlock[] multiReturnOperations(MatrixBlock in1, MatrixBlock in2, String opcode, int threads, - long seed) { + long seed) { - switch(opcode) { + switch (opcode) { case "ifft": return computeIFFT(in1, in2); default: @@ -155,8 +157,8 @@ public static MatrixBlock[] multiReturnOperations(MatrixBlock in1, MatrixBlock i } public static MatrixBlock matrixMatrixOperations(MatrixBlock in1, MatrixBlock in2, String opcode) { - if(opcode.equals("solve")) { - if(in1.getNumRows() != in1.getNumColumns()) + if (opcode.equals("solve")) { + if (in1.getNumRows() != in1.getNumColumns()) throw new DMLRuntimeException("The A matrix, in solve(A,b) should have squared dimensions."); return computeSolve(in1, in2); } @@ -178,8 +180,9 @@ private static MatrixBlock computeSolve(MatrixBlock in1, MatrixBlock in2) { BlockRealMatrix vectorInput = DataConverter.convertToBlockRealMatrix(in2); /* - * LUDecompositionImpl ludecompose = new LUDecompositionImpl(matrixInput); DecompositionSolver lusolver = - * ludecompose.getSolver(); RealMatrix solutionMatrix = lusolver.solve(vectorInput); + * LUDecompositionImpl ludecompose = new LUDecompositionImpl(matrixInput); + * DecompositionSolver lusolver = ludecompose.getSolver(); + * RealMatrix solutionMatrix = lusolver.solve(vectorInput); */ // Setup a solver based on QR Decomposition @@ -209,7 +212,7 @@ private static MatrixBlock[] computeQR(MatrixBlock in) { MatrixBlock mbH = DataConverter.convertToMatrixBlock(H.getData()); MatrixBlock mbR = DataConverter.convertToMatrixBlock(R.getData()); - return new MatrixBlock[] {mbH, mbR}; + return new MatrixBlock[] { mbH, mbR }; } /** @@ -219,10 +222,10 @@ private static MatrixBlock[] computeQR(MatrixBlock in) { * @return array of matrix blocks */ private static MatrixBlock[] computeLU(MatrixBlock in) { - if(in.getNumRows() != in.getNumColumns()) { + if (in.getNumRows() != in.getNumColumns()) { throw new DMLRuntimeException( - "LU Decomposition can only be done on a square matrix. Input matrix is rectangular (rows=" - + in.getNumRows() + ", cols=" + in.getNumColumns() + ")"); + "LU Decomposition can only be done on a square matrix. Input matrix is rectangular (rows=" + + in.getNumRows() + ", cols=" + in.getNumColumns() + ")"); } Array2DRowRealMatrix matrixInput = DataConverter.convertToArray2DRowRealMatrix(in); @@ -238,27 +241,27 @@ private static MatrixBlock[] computeLU(MatrixBlock in) { MatrixBlock mbL = DataConverter.convertToMatrixBlock(L.getData()); MatrixBlock mbU = DataConverter.convertToMatrixBlock(U.getData()); - return new MatrixBlock[] {mbP, mbL, mbU}; + return new MatrixBlock[] { mbP, mbL, mbU }; } /** - * Function to perform Eigen decomposition on a given matrix. Input must be a symmetric matrix. + * Function to perform Eigen decomposition on a given matrix. + * Input must be a symmetric matrix. * * @param in matrix object * @return array of matrix blocks */ private static MatrixBlock[] computeEigen(MatrixBlock in) { - if(in.getNumRows() != in.getNumColumns()) { + if (in.getNumRows() != in.getNumColumns()) { throw new DMLRuntimeException("Eigen Decomposition can only be done on a square matrix. " - + "Input matrix is rectangular (rows=" + in.getNumRows() + ", cols=" + in.getNumColumns() + ")"); + + "Input matrix is rectangular (rows=" + in.getNumRows() + ", cols=" + in.getNumColumns() + ")"); } EigenDecomposition eigendecompose = null; try { Array2DRowRealMatrix matrixInput = DataConverter.convertToArray2DRowRealMatrix(in); eigendecompose = new EigenDecomposition(matrixInput); - } - catch(MaxCountExceededException ex) { + } catch (MaxCountExceededException ex) { LOG.warn("Eigen: " + ex.getMessage() + ". Falling back to regularized eigen factorization."); eigendecompose = computeEigenRegularized(in); } @@ -271,68 +274,70 @@ private static MatrixBlock[] computeEigen(MatrixBlock in) { } private static EigenDecomposition computeEigenRegularized(MatrixBlock in) { - if(in == null || in.isEmptyBlock(false)) + if (in == null || in.isEmptyBlock(false)) throw new DMLRuntimeException("Invalid empty block"); // slightly modify input for regularization (pos/neg) MatrixBlock in2 = new MatrixBlock(in, false); DenseBlock a = in2.getDenseBlock(); - for(int i = 0; i < in2.rlen; i++) { + for (int i = 0; i < in2.rlen; i++) { double[] avals = a.values(i); int apos = a.pos(i); - for(int j = 0; j < in2.clen; j++) { + for (int j = 0; j < in2.clen; j++) { double v = avals[apos + j]; avals[apos + j] += Math.signum(v) * EIGEN_LAMBDA; } } // run eigen decomposition - return new EigenDecomposition(DataConverter.convertToArray2DRowRealMatrix(in2)); + return new EigenDecomposition( + DataConverter.convertToArray2DRowRealMatrix(in2)); } /** * Function to perform FFT on a given matrix. * - * @param re matrix object for real values + * @param in matrix object * @return array of matrix blocks */ - private static MatrixBlock[] computeFFT(MatrixBlock re) { - if(re == null || re.isEmptyBlock(false)) + private static MatrixBlock[] computeFFT(MatrixBlock in) { + if (in == null || in.isEmptyBlock(false)) throw new DMLRuntimeException("Invalid empty block"); // run fft - re.sparseToDense(); - return fft(re); + in.sparseToDense(); + return fft(in); } /** * Function to perform IFFT on a given matrix. * - * @param re matrix object for the real part of the values - * @param im matrix object for the imaginary part of the values + * @param in matrix object * @return array of matrix blocks */ - private static MatrixBlock[] computeIFFT(MatrixBlock re, MatrixBlock im) { - if(re == null || re.isEmptyBlock(false)) + private static MatrixBlock[] computeIFFT(MatrixBlock in1, MatrixBlock in2) { + if (in1 == null || in1.isEmptyBlock(false)) throw new DMLRuntimeException("Invalid empty block"); // run ifft - if(im != null) { - re.sparseToDense(); - im.sparseToDense(); - return ifft(re, im); - } - else { - re.sparseToDense(); - return ifft(re); + if (in2 != null) { + in1.sparseToDense(); + in2.sparseToDense(); + return ifft(in1, in2); + } else { + in1.sparseToDense(); + return ifft(in1); } } /** - * Performs Singular Value Decomposition. Calls Apache Commons Math SVD. X = U * Sigma * Vt, where X is the input - * matrix, U is the left singular matrix, Sigma is the singular values matrix returned as a column matrix and Vt is - * the transpose of the right singular matrix V. However, the returned array has { U, Sigma, V} + * Performs Singular Value Decomposition. Calls Apache Commons Math SVD. + * X = U * Sigma * Vt, where X is the input matrix, + * U is the left singular matrix, Sigma is the singular values matrix returned + * as a + * column matrix and Vt is the transpose of the right singular matrix V. + * However, the returned array has { U, Sigma, V} * * @param in Input matrix * @return An array containing U, Sigma & V @@ -349,7 +354,7 @@ private static MatrixBlock[] computeSvd(MatrixBlock in) { Sigma = LibMatrixReorg.diag(Sigma, new MatrixBlock(Sigma.rlen, Sigma.rlen, true)); MatrixBlock V = DataConverter.convertToMatrixBlock(v.getData()); - return new MatrixBlock[] {U, Sigma, V}; + return new MatrixBlock[] { U, Sigma, V }; } /** @@ -359,9 +364,9 @@ private static MatrixBlock[] computeSvd(MatrixBlock in) { * @return matrix block */ private static MatrixBlock computeMatrixInverse(Array2DRowRealMatrix in) { - if(!in.isSquare()) + if (!in.isSquare()) throw new DMLRuntimeException("Input to inv() must be square matrix -- given: a " + in.getRowDimension() - + "x" + in.getColumnDimension() + " matrix."); + + "x" + in.getColumnDimension() + " matrix."); QRDecomposition qrdecompose = new QRDecomposition(in); DecompositionSolver solver = qrdecompose.getSolver(); @@ -371,18 +376,18 @@ private static MatrixBlock computeMatrixInverse(Array2DRowRealMatrix in) { } /** - * Function to compute Cholesky decomposition of the given input matrix. The input must be a real symmetric - * positive-definite matrix. + * Function to compute Cholesky decomposition of the given input matrix. + * The input must be a real symmetric positive-definite matrix. * * @param in commons-math3 Array2DRowRealMatrix * @return matrix block */ private static MatrixBlock computeCholesky(Array2DRowRealMatrix in) { - if(!in.isSquare()) + if (!in.isSquare()) throw new DMLRuntimeException("Input to cholesky() must be square matrix -- given: a " - + in.getRowDimension() + "x" + in.getColumnDimension() + " matrix."); + + in.getRowDimension() + "x" + in.getColumnDimension() + " matrix."); CholeskyDecomposition cholesky = new CholeskyDecomposition(in, RELATIVE_SYMMETRY_THRESHOLD, - CholeskyDecomposition.DEFAULT_ABSOLUTE_POSITIVITY_THRESHOLD); + CholeskyDecomposition.DEFAULT_ABSOLUTE_POSITIVITY_THRESHOLD); RealMatrix rmL = cholesky.getL(); return DataConverter.convertToMatrixBlock(rmL.getData()); } @@ -404,16 +409,18 @@ private static MatrixBlock randNormalizedVect(int dim, int threads, long seed) { UnaryOperator op_sqrt = new UnaryOperator(Builtin.getBuiltinFnObject(Builtin.BuiltinCode.SQRT), threads, true); v1 = v1.unaryOperations(op_sqrt, new MatrixBlock()); - if(Math.abs(v1.sumSq() - 1.0) >= 1e-7) + if (Math.abs(v1.sumSq() - 1.0) >= 1e-7) throw new DMLRuntimeException("v1 not correctly normalized (maybe try changing the seed)"); return v1; } /** - * Function to perform the Lanczos algorithm and then computes the Eigendecomposition. Caution: Lanczos is not - * numerically stable (see https://en.wikipedia.org/wiki/Lanczos_algorithm) Input must be a symmetric (and square) - * matrix. + * Function to perform the Lanczos algorithm and then computes the + * Eigendecomposition. + * Caution: Lanczos is not numerically stable (see + * https://en.wikipedia.org/wiki/Lanczos_algorithm) + * Input must be a symmetric (and square) matrix. * * @param in matrix object * @param threads number of threads @@ -421,10 +428,11 @@ private static MatrixBlock randNormalizedVect(int dim, int threads, long seed) { * @return array of matrix blocks */ private static MatrixBlock[] computeEigenLanczos(MatrixBlock in, int threads, long seed) { - if(in.getNumRows() != in.getNumColumns()) { + if (in.getNumRows() != in.getNumColumns()) { throw new DMLRuntimeException( - "Lanczos algorithm and Eigen Decomposition can only be done on a square matrix. " - + "Input matrix is rectangular (rows=" + in.getNumRows() + ", cols=" + in.getNumColumns() + ")"); + "Lanczos algorithm and Eigen Decomposition can only be done on a square matrix. " + + "Input matrix is rectangular (rows=" + in.getNumRows() + ", cols=" + in.getNumColumns() + + ")"); } int m = in.getNumRows(); @@ -441,12 +449,12 @@ private static MatrixBlock[] computeEigenLanczos(MatrixBlock in, int threads, lo ScalarOperator op_div_scalar = new RightScalarOperator(Divide.getDivideFnObject(), 1, threads); MatrixBlock beta = new MatrixBlock(1, 1, 0.0); - for(int i = 0; i < m; i++) { + for (int i = 0; i < m; i++) { v1.putInto(TV, 0, i, false); w1 = in.aggregateBinaryOperations(in, v1, op_mul_agg); MatrixBlock alpha = w1.aggregateBinaryOperations(v1.reorgOperations(op_t, new MatrixBlock(), 0, 0, m), w1, - op_mul_agg); - if(i < m - 1) { + op_mul_agg); + if (i < m - 1) { w1 = w1.ternaryOperations(op_minus_mul, v1, alpha, new MatrixBlock()); w1 = w1.ternaryOperations(op_minus_mul, v0, beta, new MatrixBlock()); beta.setValue(0, 0, Math.sqrt(w1.sumSq())); @@ -467,17 +475,19 @@ private static MatrixBlock[] computeEigenLanczos(MatrixBlock in, int threads, lo } /** - * Function to perform the QR decomposition. Input must be a square matrix. TODO: use Householder transformation and - * implicit shifts to further speed up QR decompositions + * Function to perform the QR decomposition. + * Input must be a square matrix. + * TODO: use Householder transformation and implicit shifts to further speed up + * QR decompositions * * @param in matrix object * @param threads number of threads * @return array of matrix blocks [Q, R] */ private static MatrixBlock[] computeQR2(MatrixBlock in, int threads) { - if(in.getNumRows() != in.getNumColumns()) { + if (in.getNumRows() != in.getNumColumns()) { throw new DMLRuntimeException("QR2 Decomposition can only be done on a square matrix. " - + "Input matrix is rectangular (rows=" + in.getNumRows() + ", cols=" + in.getNumColumns() + ")"); + + "Input matrix is rectangular (rows=" + in.getNumRows() + ", cols=" + in.getNumColumns() + ")"); } int m = in.rlen; @@ -486,7 +496,7 @@ private static MatrixBlock[] computeQR2(MatrixBlock in, int threads) { A_n.copy(in); MatrixBlock Q_n = new MatrixBlock(m, m, true); - for(int i = 0; i < m; i++) { + for (int i = 0; i < m; i++) { Q_n.setValue(i, i, 1.0); } @@ -496,7 +506,7 @@ private static MatrixBlock[] computeQR2(MatrixBlock in, int threads) { ScalarOperator op_div_scalar = new RightScalarOperator(Divide.getDivideFnObject(), 1, threads); ScalarOperator op_mult_2 = new LeftScalarOperator(Multiply.getMultiplyFnObject(), 2, threads); - for(int k = 0; k < m; k++) { + for (int k = 0; k < m; k++) { MatrixBlock z = A_n.slice(k, m - 1, k, k); MatrixBlock uk = new MatrixBlock(m - k, 1, 0.0); uk.copy(z); @@ -515,12 +525,13 @@ private static MatrixBlock[] computeQR2(MatrixBlock in, int threads) { Q_n = Q_n.binaryOperations(op_sub, Q_n.aggregateBinaryOperations(Q_n, vkvkt2, op_mul_agg)); } // QR decomp: Q: Q_n; R: A_n - return new MatrixBlock[] {Q_n, A_n}; + return new MatrixBlock[] { Q_n, A_n }; } /** - * Function that computes the Eigen Decomposition using the QR algorithm. Caution: check if the QR algorithm is - * converged, if not increase iterations Caution: if the input matrix has complex eigenvalues results will be + * Function that computes the Eigen Decomposition using the QR algorithm. + * Caution: check if the QR algorithm is converged, if not increase iterations + * Caution: if the input matrix has complex eigenvalues results will be * incorrect * * @param in Input matrix @@ -532,20 +543,20 @@ private static MatrixBlock[] computeEigenQR(MatrixBlock in, int threads) { } private static MatrixBlock[] computeEigenQR(MatrixBlock in, int num_iterations, double tol, int threads) { - if(in.getNumRows() != in.getNumColumns()) { + if (in.getNumRows() != in.getNumColumns()) { throw new DMLRuntimeException("Eigen Decomposition (QR) can only be done on a square matrix. " - + "Input matrix is rectangular (rows=" + in.getNumRows() + ", cols=" + in.getNumColumns() + ")"); + + "Input matrix is rectangular (rows=" + in.getNumRows() + ", cols=" + in.getNumColumns() + ")"); } int m = in.rlen; AggregateBinaryOperator op_mul_agg = InstructionUtils.getMatMultOperator(threads); MatrixBlock Q_prod = new MatrixBlock(m, m, 0.0); - for(int i = 0; i < m; i++) { + for (int i = 0; i < m; i++) { Q_prod.setValue(i, i, 1.0); } - for(int i = 0; i < num_iterations; i++) { + for (int i = 0; i < num_iterations; i++) { MatrixBlock[] QR = computeQR2(in, threads); Q_prod = Q_prod.aggregateBinaryOperations(Q_prod, QR[0], op_mul_agg); in = QR[1].aggregateBinaryOperations(QR[1], QR[0], op_mul_agg); @@ -556,7 +567,7 @@ private static MatrixBlock[] computeEigenQR(MatrixBlock in, int num_iterations, double[] check = in.getDenseBlockValues(); double[] eval = new double[m]; - for(int i = 0; i < m; i++) + for (int i = 0; i < m; i++) eval[i] = check[i * m + i]; double[] evec = Q_prod.getDenseBlockValues(); @@ -577,14 +588,14 @@ private static MatrixBlock computeHouseholder(MatrixBlock in, int threads) { MatrixBlock A_n = new MatrixBlock(m, m, 0.0); A_n.copy(in); - for(int k = 0; k < m - 2; k++) { + for (int k = 0; k < m - 2; k++) { MatrixBlock ajk = A_n.slice(0, m - 1, k, k); - for(int i = 0; i <= k; i++) { + for (int i = 0; i <= k; i++) { ajk.setValue(i, 0, 0.0); } double alpha = Math.sqrt(ajk.sumSq()); double ak1k = A_n.getDouble(k + 1, k); - if(ak1k > 0.0) + if (ak1k > 0.0) alpha *= -1; double r = Math.sqrt(0.5 * (alpha * alpha - ak1k * alpha)); MatrixBlock v = new MatrixBlock(m, 1, 0.0); @@ -594,7 +605,7 @@ private static MatrixBlock computeHouseholder(MatrixBlock in, int threads) { v = v.scalarOperations(op_div_scalar, new MatrixBlock()); MatrixBlock P = new MatrixBlock(m, m, 0.0); - for(int i = 0; i < m; i++) { + for (int i = 0; i < m; i++) { P.setValue(i, i, 1.0); } @@ -613,7 +624,8 @@ private static MatrixBlock computeHouseholder(MatrixBlock in, int threads) { } /** - * Sort the eigen values (and vectors) in increasing order (to be compatible w/ LAPACK.DSYEVR()) + * Sort the eigen values (and vectors) in increasing order (to be compatible w/ + * LAPACK.DSYEVR()) * * @param eValues Eigenvalues * @param eVectors Eigenvectors @@ -621,19 +633,19 @@ private static MatrixBlock computeHouseholder(MatrixBlock in, int threads) { */ private static MatrixBlock[] sortEVs(double[] eValues, double[][] eVectors) { int n = eValues.length; - for(int i = 0; i < n; i++) { + for (int i = 0; i < n; i++) { int k = i; double p = eValues[i]; - for(int j = i + 1; j < n; j++) { - if(eValues[j] < p) { + for (int j = i + 1; j < n; j++) { + if (eValues[j] < p) { k = j; p = eValues[j]; } } - if(k != i) { + if (k != i) { eValues[k] = eValues[i]; eValues[i] = p; - for(int j = 0; j < n; j++) { + for (int j = 0; j < n; j++) { p = eVectors[j][i]; eVectors[j][i] = eVectors[j][k]; eVectors[j][k] = p; @@ -643,24 +655,24 @@ private static MatrixBlock[] sortEVs(double[] eValues, double[][] eVectors) { MatrixBlock eval = DataConverter.convertToMatrixBlock(eValues, true); MatrixBlock evec = DataConverter.convertToMatrixBlock(eVectors); - return new MatrixBlock[] {eval, evec}; + return new MatrixBlock[] { eval, evec }; } private static MatrixBlock[] sortEVs(double[] eValues, double[] eVectors) { int n = eValues.length; - for(int i = 0; i < n; i++) { + for (int i = 0; i < n; i++) { int k = i; double p = eValues[i]; - for(int j = i + 1; j < n; j++) { - if(eValues[j] < p) { + for (int j = i + 1; j < n; j++) { + if (eValues[j] < p) { k = j; p = eValues[j]; } } - if(k != i) { + if (k != i) { eValues[k] = eValues[i]; eValues[i] = p; - for(int j = 0; j < n; j++) { + for (int j = 0; j < n; j++) { p = eVectors[j * n + i]; eVectors[j * n + i] = eVectors[j * n + k]; eVectors[j * n + k] = p; @@ -671,6 +683,6 @@ private static MatrixBlock[] sortEVs(double[] eValues, double[] eVectors) { MatrixBlock eval = DataConverter.convertToMatrixBlock(eValues, true); MatrixBlock evec = new MatrixBlock(n, n, false); evec.init(eVectors, n, n); - return new MatrixBlock[] {eval, evec}; + return new MatrixBlock[] { eval, evec }; } } diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java index 19e27b983f2..7285fd14e2d 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java @@ -24,90 +24,92 @@ public class LibMatrixFourier { /** - * Function to perform FFT on two given matrices. The first one represents the real values and the second one the - * imaginary values. The output also contains one matrix for the real and one for the imaginary values. + * Function to perform FFT on two given matrices. + * The first one represents the real values and the second one the imaginary values. + * The output also contains one matrix for the real and one for the imaginary values. * * @param re matrix object representing the real values * @param im matrix object representing the imaginary values * @return array of two matrix blocks */ - public static MatrixBlock[] fft(MatrixBlock re, MatrixBlock im) { + public static MatrixBlock[] fft(MatrixBlock re, MatrixBlock im){ int rows = re.getNumRows(); int cols = re.getNumColumns(); - if(!isPowerOfTwo(rows) || !isPowerOfTwo(cols)) - throw new RuntimeException("false dimensions"); + if(!isPowerOfTwo(rows) || !isPowerOfTwo(cols)) throw new RuntimeException("false dimensions"); fft(re.getDenseBlockValues(), im.getDenseBlockValues(), rows, cols); - return new MatrixBlock[] {re, im}; + return new MatrixBlock[]{re, im}; } /** - * Function to perform IFFT on two given matrices. The first one represents the real values and the second one the - * imaginary values. The output also contains one matrix for the real and one for the imaginary values. + * Function to perform IFFT on two given matrices. + * The first one represents the real values and the second one the imaginary values. + * The output also contains one matrix for the real and one for the imaginary values. * * @param re matrix object representing the real values * @param im matrix object representing the imaginary values * @return array of two matrix blocks */ - public static MatrixBlock[] ifft(MatrixBlock re, MatrixBlock im) { + public static MatrixBlock[] ifft(MatrixBlock re, MatrixBlock im){ int rows = re.getNumRows(); int cols = re.getNumColumns(); - if(!isPowerOfTwo(rows) || !isPowerOfTwo(cols)) - throw new RuntimeException("false dimensions"); + if(!isPowerOfTwo(rows) || !isPowerOfTwo(cols)) throw new RuntimeException("false dimensions"); ifft(re.getDenseBlockValues(), im.getDenseBlockValues(), rows, cols); - return new MatrixBlock[] {re, im}; + return new MatrixBlock[]{re, im}; } /** - * Function to perform FFT on two given double arrays. The first one represents the real values and the second one - * the imaginary values. Both arrays get updated and contain the result. + * Function to perform FFT on two given double arrays. + * The first one represents the real values and the second one the imaginary values. + * Both arrays get updated and contain the result. * - * @param re array representing the real values - * @param im array representing the imaginary values + * @param re array representing the real values + * @param im array representing the imaginary values * @param rows number of rows * @param cols number of rows */ public static void fft(double[] re, double[] im, int rows, int cols) { - double[] re_inter = new double[rows * cols]; - double[] im_inter = new double[rows * cols]; + double[] re_inter = new double[rows*cols]; + double[] im_inter = new double[rows*cols]; - for(int i = 0; i < rows; i++) { - fft_one_dim(re, im, re_inter, im_inter, i * cols, (i + 1) * cols, cols, 1); + for(int i = 0; i < rows; i++){ + fft_one_dim(re, im, re_inter, im_inter, i*cols, (i+1)*cols, cols, 1); } - for(int j = 0; j < cols; j++) { - fft_one_dim(re, im, re_inter, im_inter, j, j + rows * cols, rows, cols); + for(int j = 0; j < cols; j++){ + fft_one_dim(re, im, re_inter, im_inter, j, j+rows*cols, rows, cols); } } /** - * Function to perform IFFT on two given double arrays. The first one represents the real values and the second one - * the imaginary values. Both arrays get updated and contain the result. + * Function to perform IFFT on two given double arrays. + * The first one represents the real values and the second one the imaginary values. + * Both arrays get updated and contain the result. * - * @param re array representing the real values - * @param im array representing the imaginary values + * @param re array representing the real values + * @param im array representing the imaginary values * @param rows number of rows * @param cols number of rows */ public static void ifft(double[] re, double[] im, int rows, int cols) { - double[] re_inter = new double[rows * cols]; - double[] im_inter = new double[rows * cols]; + double[] re_inter = new double[rows*cols]; + double[] im_inter = new double[rows*cols]; - for(int j = 0; j < cols; j++) { - ifft_one_dim(re, im, re_inter, im_inter, j, j + rows * cols, rows, cols); + for(int j = 0; j < cols; j++){ + ifft_one_dim(re, im, re_inter, im_inter, j, j+rows*cols, rows, cols); } - for(int i = 0; i < rows; i++) { - ifft_one_dim(re, im, re_inter, im_inter, i * cols, (i + 1) * cols, cols, 1); + for(int i = 0; i < rows; i++){ + ifft_one_dim(re, im, re_inter, im_inter, i*cols, (i+1)*cols, cols, 1); } } @@ -129,27 +131,25 @@ public static void fft_one_dim(double[] re, double[] im, double[] re_inter, doub int num, int minStep) { // start inclusive, stop exclusive - if(num == 1) - return; + if(num == 1) return; // even indices - for(int step = minStep * (num / 2), subNum = 2; subNum <= num; step /= 2, subNum *= 2) { + for(int step = minStep*(num/2), subNum = 2; subNum <= num; step/=2, subNum*=2){ - double angle = -2 * FastMath.PI / subNum; + double angle = -2*FastMath.PI/subNum; // use ceil for the main (sub)array - for(int sub = 0; sub < FastMath.ceil(num / (2 * (double) subNum)); sub++) { + for(int sub = 0; sub < FastMath.ceil(num/(2*(double)subNum)); sub++){ for(int isOdd = 0; isOdd < 2; isOdd++) { // no odd values for main (sub)array - if(isOdd == 1 && subNum == num) - return; + if (isOdd == 1 && subNum == num) return; - int startSub = start + sub * minStep + isOdd * (step / 2); + int startSub = start + sub*minStep + isOdd*(step/2); // first iterates over even indices, then over odd indices - for(int j = startSub, cnt = 0; cnt < subNum / 2; j += 2 * step, cnt++) { + for (int j = startSub, cnt = 0; cnt < subNum / 2; j += 2*step, cnt++) { double omega_pow_re = FastMath.cos(cnt * angle); double omega_pow_im = FastMath.sin(cnt * angle); @@ -160,13 +160,13 @@ public static void fft_one_dim(double[] re, double[] im, double[] re_inter, doub int index = startSub + cnt * step; re_inter[index] = re[j] + m_re; - re_inter[index + (stop - start) / 2] = re[j] - m_re; + re_inter[index + (stop-start)/2] = re[j] - m_re; im_inter[index] = im[j] + m_im; - im_inter[index + (stop - start) / 2] = im[j] - m_im; + im_inter[index + (stop-start)/2] = im[j] - m_im; } - for(int j = startSub; j < startSub + (stop - start); j += step) { + for (int j = startSub; j < startSub + (stop-start); j += step) { re[j] = re_inter[j]; im[j] = im_inter[j]; re_inter[j] = 0; @@ -181,22 +181,21 @@ public static void fft_one_dim(double[] re, double[] im, double[] re_inter, doub } - private static void ifft_one_dim(double[] re, double[] im, double[] re_inter, double[] im_inter, int start, - int stop, int num, int minStep) { + private static void ifft_one_dim(double[] re, double[] im, double[] re_inter, double[] im_inter, int start, int stop, int num, int minStep) { // conjugate input - for(int i = start; i < start + num * minStep; i += minStep) { + for (int i = start; i < start+num*minStep; i+=minStep){ im[i] = -im[i]; } // apply fft - // fft_one_dim_recursive(re, im, re_inter, im_inter, start, step, num, subArraySize); + //fft_one_dim_recursive(re, im, re_inter, im_inter, start, step, num, subArraySize); fft_one_dim(re, im, re_inter, im_inter, start, stop, num, minStep); // conjugate and scale result - for(int i = start; i < start + num * minStep; i += minStep) { - re[i] = re[i] / num; - im[i] = -im[i] / num; + for (int i = start; i < start+num*minStep; i+=minStep){ + re[i] = re[i]/num; + im[i] = -im[i]/num; } } @@ -207,32 +206,32 @@ private static void ifft_one_dim(double[] re, double[] im, double[] re_inter, do * @param n integer to check * @return true if n is a power of two, false otherwise */ - public static boolean isPowerOfTwo(int n) { + public static boolean isPowerOfTwo(int n){ return (n != 0) && ((n & (n - 1)) == 0); } /** - * Function to perform FFT on a given matrices. The given matrix only represents real values. The output contains - * one matrix for the real and one for the imaginary values. + * Function to perform FFT on a given matrices. + * The given matrix only represents real values. + * The output contains one matrix for the real and one for the imaginary values. * * @param re matrix object representing the real values * @return array of two matrix blocks */ - public static MatrixBlock[] fft(MatrixBlock re) { - return fft(re, - new MatrixBlock(re.getNumRows(), re.getNumColumns(), new double[re.getNumRows() * re.getNumColumns()])); + public static MatrixBlock[] fft(MatrixBlock re){ + return fft(re, new MatrixBlock(re.getNumRows(),re.getNumColumns(), new double[re.getNumRows()*re.getNumColumns()])); } /** - * Function to perform IFFT on a given matrices. The given matrix only represents real values. The output contains - * one matrix for the real and one for the imaginary values. + * Function to perform IFFT on a given matrices. + * The given matrix only represents real values. + * The output contains one matrix for the real and one for the imaginary values. * * @param re matrix object representing the real values * @return array of two matrix blocks */ - public static MatrixBlock[] ifft(MatrixBlock re) { - return ifft(re, - new MatrixBlock(re.getNumRows(), re.getNumColumns(), new double[re.getNumRows() * re.getNumColumns()])); + public static MatrixBlock[] ifft(MatrixBlock re){ + return ifft(re, new MatrixBlock(re.getNumRows(),re.getNumColumns(), new double[re.getNumRows()*re.getNumColumns()])); } } diff --git a/src/main/java/org/apache/sysds/runtime/privacy/propagation/PrivacyPropagator.java b/src/main/java/org/apache/sysds/runtime/privacy/propagation/PrivacyPropagator.java index 089d290582d..4d2996b7856 100644 --- a/src/main/java/org/apache/sysds/runtime/privacy/propagation/PrivacyPropagator.java +++ b/src/main/java/org/apache/sysds/runtime/privacy/propagation/PrivacyPropagator.java @@ -70,38 +70,40 @@ import org.apache.wink.json4j.JSONObject; /** - * Class with static methods merging privacy constraints of operands in expressions to generate the privacy constraints - * of the output. + * Class with static methods merging privacy constraints of operands + * in expressions to generate the privacy constraints of the output. */ public class PrivacyPropagator { /** - * Parses the privacy constraint of the given metadata object and sets the field of the given Data if the privacy - * constraint is not null. + * Parses the privacy constraint of the given metadata object + * and sets the field of the given Data if the privacy constraint is not null. * * @param cd data for which privacy constraint is set * @param mtd metadata object * @return data object with privacy constraint set * @throws JSONException during parsing of metadata */ - public static Data parseAndSetPrivacyConstraint(Data cd, JSONObject mtd) throws JSONException { + public static Data parseAndSetPrivacyConstraint(Data cd, JSONObject mtd) + throws JSONException { PrivacyConstraint mtdPrivConstraint = parseAndReturnPrivacyConstraint(mtd); - if(mtdPrivConstraint != null) + if (mtdPrivConstraint != null) cd.setPrivacyConstraints(mtdPrivConstraint); return cd; } /** - * Parses the privacy constraint of the given metadata object or returns null if no privacy constraint is set in the - * metadata. + * Parses the privacy constraint of the given metadata object + * or returns null if no privacy constraint is set in the metadata. * * @param mtd metadata * @return privacy constraint parsed from metadata object * @throws JSONException during parsing of metadata */ - public static PrivacyConstraint parseAndReturnPrivacyConstraint(JSONObject mtd) throws JSONException { - if(mtd.containsKey(DataExpression.PRIVACY)) { + public static PrivacyConstraint parseAndReturnPrivacyConstraint(JSONObject mtd) + throws JSONException { + if (mtd.containsKey(DataExpression.PRIVACY)) { String privacyLevel = mtd.getString(DataExpression.PRIVACY); - if(privacyLevel != null) + if (privacyLevel != null) return new PrivacyConstraint(PrivacyLevel.valueOf(privacyLevel)); } return null; @@ -112,67 +114,84 @@ private static boolean anyInputHasLevel(PrivacyLevel[] inputLevels, PrivacyLevel } /** - * Returns the output privacy level based on the given input privacy levels and operator type. It represents the - * basic logic of privacy propagation: + * Returns the output privacy level based on the given input privacy levels and + * operator type. + * It represents the basic logic of privacy propagation: * - * Unary input: Input | NonAggregate | Aggregate ----------------------------------- priv | priv | priv privAgg | - * privAgg | none none | none | none + * Unary input: + * Input | NonAggregate | Aggregate + * ----------------------------------- + * priv | priv | priv + * privAgg | privAgg | none + * none | none | none * - * Binary input: Input | NonAggregate | Aggregate -------------------------------------------- priv-priv | priv | - * priv priv-privAgg | priv | priv priv-none | priv | priv privAgg-priv | priv | priv none-priv | priv | priv - * privAgg-privAgg | privAgg | none none-none | none | none privAgg-none | privAgg | none none-privAgg | privAgg | - * none + * Binary input: + * Input | NonAggregate | Aggregate + * -------------------------------------------- + * priv-priv | priv | priv + * priv-privAgg | priv | priv + * priv-none | priv | priv + * privAgg-priv | priv | priv + * none-priv | priv | priv + * privAgg-privAgg | privAgg | none + * none-none | none | none + * privAgg-none | privAgg | none + * none-privAgg | privAgg | none * * @param inputLevels privacy levels of the input - * @param operatorType type of the operator which is either an aggregation (Aggregate) or not an aggregation - * (NonAggregate) + * @param operatorType type of the operator which is either an aggregation + * (Aggregate) or not an aggregation (NonAggregate) * @return output privacy level */ public static PrivacyLevel corePropagation(PrivacyLevel[] inputLevels, OperatorType operatorType) { - if(anyInputHasLevel(inputLevels, PrivacyLevel.Private)) + if (anyInputHasLevel(inputLevels, PrivacyLevel.Private)) return PrivacyLevel.Private; - if(operatorType == OperatorType.Aggregate) + if (operatorType == OperatorType.Aggregate) return PrivacyLevel.None; - if(operatorType == OperatorType.NonAggregate && anyInputHasLevel(inputLevels, PrivacyLevel.PrivateAggregation)) + if (operatorType == OperatorType.NonAggregate && anyInputHasLevel(inputLevels, PrivacyLevel.PrivateAggregation)) return PrivacyLevel.PrivateAggregation; return PrivacyLevel.None; } /** - * Merges the given privacy constraints with the core propagation using the given operator type. + * Merges the given privacy constraints with the core propagation using the + * given operator type. * * @param privacyConstraints array of privacy constraints to merge - * @param operatorType type of operation to use when merging with the core propagation + * @param operatorType type of operation to use when merging with the core + * propagation * @return merged privacy constraint */ private static PrivacyConstraint mergeNary(PrivacyConstraint[] privacyConstraints, OperatorType operatorType) { - PrivacyLevel[] privacyLevels = Arrays.stream(privacyConstraints).map(constraint -> { - if(constraint != null) - return constraint.getPrivacyLevel(); - else - return PrivacyLevel.None; - }).toArray(PrivacyLevel[]::new); + PrivacyLevel[] privacyLevels = Arrays.stream(privacyConstraints) + .map(constraint -> { + if (constraint != null) + return constraint.getPrivacyLevel(); + else + return PrivacyLevel.None; + }) + .toArray(PrivacyLevel[]::new); PrivacyLevel outputPrivacyLevel = corePropagation(privacyLevels, operatorType); return new PrivacyConstraint(outputPrivacyLevel); } /** - * Merges the input privacy constraints using the core propagation with NonAggregate operator type. + * Merges the input privacy constraints using the core propagation with + * NonAggregate operator type. * * @param privacyConstraint1 first privacy constraint * @param privacyConstraint2 second privacy constraint * @return merged privacy constraint */ public static PrivacyConstraint mergeBinary(PrivacyConstraint privacyConstraint1, - PrivacyConstraint privacyConstraint2) { - if(privacyConstraint1 != null && privacyConstraint2 != null) { - PrivacyLevel[] privacyLevels = new PrivacyLevel[] {privacyConstraint1.getPrivacyLevel(), - privacyConstraint2.getPrivacyLevel()}; + PrivacyConstraint privacyConstraint2) { + if (privacyConstraint1 != null && privacyConstraint2 != null) { + PrivacyLevel[] privacyLevels = new PrivacyLevel[] { + privacyConstraint1.getPrivacyLevel(), privacyConstraint2.getPrivacyLevel() }; return new PrivacyConstraint(corePropagation(privacyLevels, OperatorType.NonAggregate)); - } - else if(privacyConstraint1 != null) + } else if (privacyConstraint1 != null) return privacyConstraint1; - else if(privacyConstraint2 != null) + else if (privacyConstraint2 != null) return privacyConstraint2; return null; } @@ -193,35 +212,37 @@ public static void hopPropagation(Hop hop) { * @param inputHops inputs to given hop */ public static void hopPropagation(Hop hop, ArrayList inputHops) { - PrivacyConstraint[] inputConstraints = inputHops.stream().map(Hop::getPrivacy) - .toArray(PrivacyConstraint[]::new); + PrivacyConstraint[] inputConstraints = inputHops.stream() + .map(Hop::getPrivacy).toArray(PrivacyConstraint[]::new); OperatorType opType = getOpType(hop); hop.setPrivacy(mergeNary(inputConstraints, opType)); - if(opType == null && Arrays.stream(inputConstraints).anyMatch(Objects::nonNull)) - throw new DMLException("Input has constraint but hop type not recognized by PrivacyPropagator. " + "Hop is " - + hop + " " + hop.getClass()); + if (opType == null && Arrays.stream(inputConstraints).anyMatch(Objects::nonNull)) + throw new DMLException("Input has constraint but hop type not recognized by PrivacyPropagator. " + + "Hop is " + hop + " " + hop.getClass()); } /** - * Get operator type of given hop. Returns null if hop type is not known. + * Get operator type of given hop. + * Returns null if hop type is not known. * * @param hop for which operator type is returned * @return operator type of hop or null if hop type is unknown */ private static OperatorType getOpType(Hop hop) { - if(hop instanceof TernaryOp || hop instanceof BinaryOp || hop instanceof ReorgOp || hop instanceof DataOp || - hop instanceof LiteralOp || hop instanceof NaryOp || hop instanceof DataGenOp || - hop instanceof FunctionOp || hop instanceof IndexingOp || hop instanceof ParameterizedBuiltinOp || - hop instanceof LeftIndexingOp) + if (hop instanceof TernaryOp || hop instanceof BinaryOp || hop instanceof ReorgOp + || hop instanceof DataOp || hop instanceof LiteralOp || hop instanceof NaryOp + || hop instanceof DataGenOp || hop instanceof FunctionOp || hop instanceof IndexingOp + || hop instanceof ParameterizedBuiltinOp || hop instanceof LeftIndexingOp) return OperatorType.NonAggregate; - else if(hop instanceof AggBinaryOp || hop instanceof AggUnaryOp || hop instanceof UnaryOp) + else if (hop instanceof AggBinaryOp || hop instanceof AggUnaryOp || hop instanceof UnaryOp) return OperatorType.Aggregate; else return null; } /** - * Propagate privacy constraints to output variables based on privacy constraint of CPOperand output in instruction + * Propagate privacy constraints to output variables + * based on privacy constraint of CPOperand output in instruction * which has been set during privacy propagation preprocessing. * * @param inst instruction for which privacy constraints are propagated @@ -230,25 +251,26 @@ else if(hop instanceof AggBinaryOp || hop instanceof AggUnaryOp || hop instanceo public static void postProcessInstruction(Instruction inst, ExecutionContext ec) { // if inst has output List instOutputs = getOutputOperands(inst); - if(!instOutputs.isEmpty()) { - for(CPOperand output : instOutputs) { + if (!instOutputs.isEmpty()) { + for (CPOperand output : instOutputs) { PrivacyConstraint outputPrivacyConstraint = output.getPrivacyConstraint(); - if(PrivacyUtils.someConstraintSetUnary(outputPrivacyConstraint)) + if (PrivacyUtils.someConstraintSetUnary(outputPrivacyConstraint)) setOutputPrivacyConstraint(ec, outputPrivacyConstraint, output.getName()); } } } /** - * Propagate privacy constraints from input to output CPOperands in case the privacy constraints of the input are - * activated. + * Propagate privacy constraints from input to output CPOperands + * in case the privacy constraints of the input are activated. * * @param inst instruction for which the privacy constraints are propagated * @param ec execution context - * @return instruction with propagated privacy constraints (usually the same instance as the input inst) + * @return instruction with propagated privacy constraints (usually the same + * instance as the input inst) */ public static Instruction preprocessInstruction(Instruction inst, ExecutionContext ec) { - switch(inst.getType()) { + switch (inst.getType()) { case CONTROL_PROGRAM: return preprocessCPInstruction((CPInstruction) inst, ec); case BREAKPOINT: @@ -262,7 +284,7 @@ public static Instruction preprocessInstruction(Instruction inst, ExecutionConte } private static Instruction preprocessCPInstruction(CPInstruction inst, ExecutionContext ec) { - switch(inst.getCPInstructionType()) { + switch (inst.getCPInstructionType()) { case Binary: case Builtin: case BuiltinNary: @@ -283,7 +305,7 @@ private static Instruction preprocessCPInstruction(CPInstruction inst, Execution case Append: return preprocessAppendCPInstruction((AppendCPInstruction) inst, ec); case AggregateBinary: - if(inst instanceof AggregateBinaryCPInstruction) + if (inst instanceof AggregateBinaryCPInstruction) return preprocessAggregateBinaryCPInstruction((AggregateBinaryCPInstruction) inst, ec); else return throwExceptionIfInputOrInstPrivacy(inst, ec); @@ -301,7 +323,7 @@ private static Instruction preprocessCPInstruction(CPInstruction inst, Execution } private static Instruction preprocessVariableCPInstruction(VariableCPInstruction inst, ExecutionContext ec) { - switch(inst.getVariableOpcode()) { + switch (inst.getVariableOpcode()) { case CopyVariable: case MoveVariable: case RemoveVariableAndFile: @@ -329,28 +351,28 @@ private static Instruction preprocessVariableCPInstruction(VariableCPInstruction } /** - * Propagates fine-grained constraints if input has fine-grained constraints, otherwise it propagates general - * constraints. + * Propagates fine-grained constraints if input has fine-grained constraints, + * otherwise it propagates general constraints. * * @param inst aggregate binary instruction for which constraints are propagated * @param ec execution context - * @return instruction with merged privacy constraints propagated to it and output CPOperand + * @return instruction with merged privacy constraints propagated to it and + * output CPOperand */ private static Instruction preprocessAggregateBinaryCPInstruction(AggregateBinaryCPInstruction inst, - ExecutionContext ec) { + ExecutionContext ec) { PrivacyConstraint[] privacyConstraints = getInputPrivacyConstraints(ec, inst.getInputs()); - if(PrivacyUtils.someConstraintSetBinary(privacyConstraints)) { + if (PrivacyUtils.someConstraintSetBinary(privacyConstraints)) { PrivacyConstraint mergedPrivacyConstraint; - if((privacyConstraints[0] != null && privacyConstraints[0].hasFineGrainedConstraints()) || - (privacyConstraints[1] != null && privacyConstraints[1].hasFineGrainedConstraints())) { + if ((privacyConstraints[0] != null && privacyConstraints[0].hasFineGrainedConstraints()) || + (privacyConstraints[1] != null && privacyConstraints[1].hasFineGrainedConstraints())) { MatrixBlock input1 = ec.getMatrixInput(inst.input1.getName()); MatrixBlock input2 = ec.getMatrixInput(inst.input2.getName()); Propagator propagator = new MatrixMultiplicationPropagatorPrivateFirst(input1, privacyConstraints[0], - input2, privacyConstraints[1]); + input2, privacyConstraints[1]); mergedPrivacyConstraint = propagator.propagate(); ec.releaseMatrixInput(inst.input1.getName(), inst.input2.getName()); - } - else { + } else { mergedPrivacyConstraint = mergeNary(privacyConstraints, OperatorType.getAggregationType(inst, ec)); inst.setPrivacyConstraint(mergedPrivacyConstraint); } @@ -360,51 +382,50 @@ private static Instruction preprocessAggregateBinaryCPInstruction(AggregateBinar } /** - * Propagates input privacy constraints using general and fine-grained constraints depending on the AppendType. + * Propagates input privacy constraints using general and fine-grained + * constraints depending on the AppendType. * * @param inst append instruction for which constraints are propagated * @param ec execution context - * @return instruction with merged privacy constraints propagated to it and output CPOperand + * @return instruction with merged privacy constraints propagated to it and + * output CPOperand */ private static Instruction preprocessAppendCPInstruction(AppendCPInstruction inst, ExecutionContext ec) { PrivacyConstraint[] privacyConstraints = getInputPrivacyConstraints(ec, inst.getInputs()); - if(PrivacyUtils.someConstraintSetBinary(privacyConstraints)) { - if(inst.getAppendType() == AppendCPInstruction.AppendType.STRING) { + if (PrivacyUtils.someConstraintSetBinary(privacyConstraints)) { + if (inst.getAppendType() == AppendCPInstruction.AppendType.STRING) { PrivacyLevel[] privacyLevels = new PrivacyLevel[2]; privacyLevels[0] = PrivacyUtils.getGeneralPrivacyLevel(privacyConstraints[0]); privacyLevels[1] = PrivacyUtils.getGeneralPrivacyLevel(privacyConstraints[1]); PrivacyConstraint outputConstraint = new PrivacyConstraint( - corePropagation(privacyLevels, OperatorType.NonAggregate)); + corePropagation(privacyLevels, OperatorType.NonAggregate)); inst.output.setPrivacyConstraint(outputConstraint); - } - else if(inst.getAppendType() == AppendCPInstruction.AppendType.LIST) { + } else if (inst.getAppendType() == AppendCPInstruction.AppendType.LIST) { ListObject input1 = (ListObject) ec.getVariable(inst.input1); - if(inst.getOpcode().equals("remove")) { + if (inst.getOpcode().equals("remove")) { ScalarObject removePosition = ec.getScalarInput(inst.input2); PropagatorMultiReturn propagator = new ListRemovePropagator(input1, privacyConstraints[0], - removePosition, removePosition.getPrivacyConstraint()); + removePosition, removePosition.getPrivacyConstraint()); PrivacyConstraint[] outputConstraints = propagator.propagate(); inst.output.setPrivacyConstraint(outputConstraints[0]); ((ListAppendRemoveCPInstruction) inst).getOutput2().setPrivacyConstraint(outputConstraints[1]); - } - else { + } else { ListObject input2 = (ListObject) ec.getVariable(inst.input2); Propagator propagator = new ListAppendPropagator(input1, privacyConstraints[0], input2, - privacyConstraints[1]); + privacyConstraints[1]); inst.output.setPrivacyConstraint(propagator.propagate()); } - } - else { + } else { MatrixBlock input1 = ec.getMatrixInput(inst.input1.getName()); MatrixBlock input2 = ec.getMatrixInput(inst.input2.getName()); Propagator propagator; - if(inst.getAppendType() == AppendCPInstruction.AppendType.RBIND) + if (inst.getAppendType() == AppendCPInstruction.AppendType.RBIND) propagator = new RBindPropagator(input1, privacyConstraints[0], input2, privacyConstraints[1]); - else if(inst.getAppendType() == AppendCPInstruction.AppendType.CBIND) + else if (inst.getAppendType() == AppendCPInstruction.AppendType.CBIND) propagator = new CBindPropagator(input1, privacyConstraints[0], input2, privacyConstraints[1]); else - throw new DMLPrivacyException("Instruction " + inst.getCPInstructionType() + " with append type " - + inst.getAppendType() + " is not supported by the privacy propagator"); + throw new DMLPrivacyException("Instruction " + inst.getCPInstructionType() + " with append type " + + inst.getAppendType() + " is not supported by the privacy propagator"); inst.output.setPrivacyConstraint(propagator.propagate()); ec.releaseMatrixInput(inst.input1.getName(), inst.input2.getName()); } @@ -413,40 +434,44 @@ else if(inst.getAppendType() == AppendCPInstruction.AppendType.CBIND) } /** - * Propagates privacy constraints from input to instruction and output CPOperand based on given operator type. The - * propagation is done through the core propagation. + * Propagates privacy constraints from input to instruction and output CPOperand + * based on given operator type. + * The propagation is done through the core propagation. * * @param inst instruction for which privacy is propagated * @param ec execution context * @param operatorType defining whether the instruction is aggregating the input - * @return instruction with the merged privacy constraint propagated to it and output CPOperand + * @return instruction with the merged privacy constraint propagated to it and + * output CPOperand */ private static Instruction mergePrivacyConstraintsFromInput(Instruction inst, ExecutionContext ec, - OperatorType operatorType) { + OperatorType operatorType) { return mergePrivacyConstraintsFromInput(inst, ec, getInputOperands(inst), getOutputOperands(inst), - operatorType); + operatorType); } /** - * Propagates privacy constraints from input to instruction and output CPOperand based on given operator type. The - * propagation is done through the core propagation. + * Propagates privacy constraints from input to instruction and output CPOperand + * based on given operator type. + * The propagation is done through the core propagation. * * @param inst instruction for which privacy is propagated * @param ec execution context * @param inputs to instruction * @param outputs of instruction * @param operatorType defining whether the instruction is aggregating the input - * @return instruction with the merged privacy constraint propagated to it and output CPOperand + * @return instruction with the merged privacy constraint propagated to it and + * output CPOperand */ private static Instruction mergePrivacyConstraintsFromInput(Instruction inst, ExecutionContext ec, - CPOperand[] inputs, List outputs, OperatorType operatorType) { - if(inputs != null && inputs.length > 0) { + CPOperand[] inputs, List outputs, OperatorType operatorType) { + if (inputs != null && inputs.length > 0) { PrivacyConstraint[] privacyConstraints = getInputPrivacyConstraints(ec, inputs); - if(privacyConstraints != null) { + if (privacyConstraints != null) { PrivacyConstraint mergedPrivacyConstraint = mergeNary(privacyConstraints, operatorType); inst.setPrivacyConstraint(mergedPrivacyConstraint); - for(CPOperand output : outputs) { - if(output != null) { + for (CPOperand output : outputs) { + if (output != null) { output.setPrivacyConstraint(mergedPrivacyConstraint); } } @@ -456,7 +481,8 @@ private static Instruction mergePrivacyConstraintsFromInput(Instruction inst, Ex } /** - * Throw exception if privacy constraint activated for instruction or for input to instruction. + * Throw exception if privacy constraint activated for instruction or for input + * to instruction. * * @param inst covariance instruction * @param ec execution context @@ -465,12 +491,12 @@ private static Instruction mergePrivacyConstraintsFromInput(Instruction inst, Ex private static Instruction throwExceptionIfInputOrInstPrivacy(Instruction inst, ExecutionContext ec) { throwExceptionIfPrivacyActivated(inst); CPOperand[] inputOperands = getInputOperands(inst); - if(inputOperands != null) { - for(CPOperand input : inputOperands) { + if (inputOperands != null) { + for (CPOperand input : inputOperands) { PrivacyConstraint privacyConstraint = getInputPrivacyConstraint(ec, input); - if(privacyConstraint != null && privacyConstraint.hasConstraints()) { + if (privacyConstraint != null && privacyConstraint.hasConstraints()) { throw new DMLPrivacyException("Input of instruction " + inst - + " has privacy constraints activated, but the constraints are not propagated during preprocessing of instruction."); + + " has privacy constraints activated, but the constraints are not propagated during preprocessing of instruction."); } } } @@ -478,14 +504,15 @@ private static Instruction throwExceptionIfInputOrInstPrivacy(Instruction inst, } private static void throwExceptionIfPrivacyActivated(Instruction inst) { - if(inst.getPrivacyConstraint() != null && inst.getPrivacyConstraint().hasConstraints()) { + if (inst.getPrivacyConstraint() != null && inst.getPrivacyConstraint().hasConstraints()) { throw new DMLPrivacyException("Instruction " + inst - + " has privacy constraints activated, but the constraints are not propagated during preprocessing of instruction."); + + " has privacy constraints activated, but the constraints are not propagated during preprocessing of instruction."); } } /** - * Propagate privacy constraint to instruction and output of instruction if data of first input is CacheableData and + * Propagate privacy constraint to instruction and output of instruction + * if data of first input is CacheableData and * privacy constraint is activated. * * @param inst VariableCPInstruction @@ -497,8 +524,9 @@ private static Instruction propagateFirstInputPrivacy(VariableCPInstruction inst } /** - * Propagate privacy constraint to instruction and output of instruction if data of second input is CacheableData - * and privacy constraint is activated. + * Propagate privacy constraint to instruction and output of instruction + * if data of second input is CacheableData and + * privacy constraint is activated. * * @param inst VariableCPInstruction * @param ec execution context @@ -509,8 +537,9 @@ private static Instruction propagateSecondInputPrivacy(VariableCPInstruction ins } /** - * Propagate privacy constraint to instruction and output of instruction if data of the specified variable is - * CacheableData and privacy constraint is activated + * Propagate privacy constraint to instruction and output of instruction + * if data of the specified variable is CacheableData + * and privacy constraint is activated * * @param inst instruction * @param ec execution context @@ -519,11 +548,11 @@ private static Instruction propagateSecondInputPrivacy(VariableCPInstruction ins * @return instruction with or without privacy constraints */ private static Instruction propagateInputPrivacy(Instruction inst, ExecutionContext ec, CPOperand inputOperand, - CPOperand outputOperand) { + CPOperand outputOperand) { PrivacyConstraint privacyConstraint = getInputPrivacyConstraint(ec, inputOperand); - if(privacyConstraint != null) { + if (privacyConstraint != null) { inst.setPrivacyConstraint(privacyConstraint); - if(outputOperand != null) + if (outputOperand != null) outputOperand.setPrivacyConstraint(privacyConstraint); } return inst; @@ -534,52 +563,56 @@ private static Instruction propagateInputPrivacy(Instruction inst, ExecutionCont * * @param ec execution context from which the data variable is retrieved * @param input data variable from which the privacy constraint is retrieved - * @return privacy constraint of variable or null if privacy constraint is not set + * @return privacy constraint of variable or null if privacy constraint is not + * set */ private static PrivacyConstraint getInputPrivacyConstraint(ExecutionContext ec, CPOperand input) { - if(input != null && input.getName() != null) { + if (input != null && input.getName() != null) { Data dd = ec.getVariable(input.getName()); - if(dd != null) + if (dd != null) return dd.getPrivacyConstraint(); } return null; } /** - * Returns input privacy constraints as array or returns null if no privacy constraints are found in the inputs. + * Returns input privacy constraints as array or returns null if no privacy + * constraints are found in the inputs. * * @param ec execution context * @param inputs from which privacy constraints are retrieved * @return array of privacy constraints from inputs */ private static PrivacyConstraint[] getInputPrivacyConstraints(ExecutionContext ec, CPOperand[] inputs) { - if(inputs != null && inputs.length > 0) { + if (inputs != null && inputs.length > 0) { boolean privacyFound = false; PrivacyConstraint[] privacyConstraints = new PrivacyConstraint[inputs.length]; - for(int i = 0; i < inputs.length; i++) { + for (int i = 0; i < inputs.length; i++) { privacyConstraints[i] = getInputPrivacyConstraint(ec, inputs[i]); - if(privacyConstraints[i] != null) + if (privacyConstraints[i] != null) privacyFound = true; } - if(privacyFound) + if (privacyFound) return privacyConstraints; } return null; } /** - * Set privacy constraint of data variable with outputName if the variable exists and the privacy constraint is not - * null. + * Set privacy constraint of data variable with outputName + * if the variable exists and the privacy constraint is not null. * - * @param ec execution context from which the data variable is retrieved + * @param ec execution context from which the data variable is + * retrieved * @param privacyConstraint privacy constraint which the variable should have - * @param outputName name of variable that is retrieved from the execution context + * @param outputName name of variable that is retrieved from the + * execution context */ private static void setOutputPrivacyConstraint(ExecutionContext ec, PrivacyConstraint privacyConstraint, - String outputName) { - if(privacyConstraint != null) { + String outputName) { + if (privacyConstraint != null) { Data dd = ec.getVariable(outputName); - if(dd != null) { + if (dd != null) { dd.setPrivacyConstraints(privacyConstraint); ec.setVariable(outputName, dd); } @@ -587,51 +620,54 @@ private static void setOutputPrivacyConstraint(ExecutionContext ec, PrivacyConst } /** - * Returns input CPOperands of instruction or returns null if instruction type is not supported by this method. + * Returns input CPOperands of instruction or returns null if instruction type + * is not supported by this method. * * @param inst instruction from which the inputs are retrieved * @return array of input CPOperands or null */ private static CPOperand[] getInputOperands(Instruction inst) { - if(inst instanceof ComputationCPInstruction) + if (inst instanceof ComputationCPInstruction) return ((ComputationCPInstruction) inst).getInputs(); - if(inst instanceof BuiltinNaryCPInstruction) + if (inst instanceof BuiltinNaryCPInstruction) return ((BuiltinNaryCPInstruction) inst).getInputs(); - if(inst instanceof FunctionCallCPInstruction) + if (inst instanceof FunctionCallCPInstruction) return ((FunctionCallCPInstruction) inst).getInputs(); - if(inst instanceof SqlCPInstruction) + if (inst instanceof SqlCPInstruction) return ((SqlCPInstruction) inst).getInputs(); else return null; } /** - * Returns a list of output CPOperands of instruction or an empty list if the instruction has no outputs. Note that - * this method needs to be extended as new instruction types are added, otherwise it will return an empty list for - * instructions that may have outputs. + * Returns a list of output CPOperands of instruction or an empty list if the + * instruction has no outputs. + * Note that this method needs to be extended as new instruction types are + * added, otherwise it will + * return an empty list for instructions that may have outputs. * * @param inst instruction from which the outputs are retrieved * @return list of outputs */ private static List getOutputOperands(Instruction inst) { // The order of the following statements is important - if(inst instanceof MultiReturnParameterizedBuiltinCPInstruction) + if (inst instanceof MultiReturnParameterizedBuiltinCPInstruction) return ((MultiReturnParameterizedBuiltinCPInstruction) inst).getOutputs(); - else if(inst instanceof MultiReturnBuiltinCPInstruction) + else if (inst instanceof MultiReturnBuiltinCPInstruction) return ((MultiReturnBuiltinCPInstruction) inst).getOutputs(); - else if(inst instanceof ComputationCPInstruction) + else if (inst instanceof ComputationCPInstruction) return getSingletonList(((ComputationCPInstruction) inst).getOutput()); - else if(inst instanceof VariableCPInstruction) + else if (inst instanceof VariableCPInstruction) return getSingletonList(((VariableCPInstruction) inst).getOutput()); - else if(inst instanceof SqlCPInstruction) + else if (inst instanceof SqlCPInstruction) return getSingletonList(((SqlCPInstruction) inst).getOutput()); - else if(inst instanceof BuiltinNaryCPInstruction) + else if (inst instanceof BuiltinNaryCPInstruction) return getSingletonList(((BuiltinNaryCPInstruction) inst).getOutput()); return new ArrayList<>(); } private static List getSingletonList(CPOperand operand) { - if(operand != null) + if (operand != null) return new ArrayList<>(Collections.singletonList(operand)); return new ArrayList<>(); } diff --git a/src/test/java/org/apache/sysds/test/component/matrix/FourierTest.java b/src/test/java/org/apache/sysds/test/component/matrix/FourierTest.java index ea279ef0bcf..2b25b54c7e5 100644 --- a/src/test/java/org/apache/sysds/test/component/matrix/FourierTest.java +++ b/src/test/java/org/apache/sysds/test/component/matrix/FourierTest.java @@ -32,8 +32,8 @@ public class FourierTest { @Test public void test_fft_one_dim() { - MatrixBlock re = new MatrixBlock(1, 4, new double[] {0, 18, -15, 3}); - MatrixBlock im = new MatrixBlock(1, 4, new double[4]); + MatrixBlock re = new MatrixBlock(1, 4, new double[]{0, 18, -15, 3}); + MatrixBlock im = new MatrixBlock(1, 4, new double[4]); double[] expected_re = {6, 15, -36, 15}; double[] expected_im = {0, -15, 0, 15}; @@ -48,8 +48,8 @@ public void test_fft_one_dim() { @Test public void test_fft_one_dim_2() { - MatrixBlock re = new MatrixBlock(1, 8, new double[] {0, 18, -15, 3, 5, 10, 5, 9}); - MatrixBlock im = new MatrixBlock(1, 8, new double[8]); + MatrixBlock re = new MatrixBlock(1, 8, new double[]{0, 18, -15, 3, 5, 10, 5, 9}); + MatrixBlock im = new MatrixBlock(1, 8, new double[8]); double[] expected_re = {35, 4.89949, 15, -14.89949, -45, -14.89949, 15, 4.89949}; double[] expected_im = {0, 18.58579, -16, -21.41421, 0, 21.41421, 16, -18.58579}; @@ -63,8 +63,8 @@ public void test_fft_one_dim_2() { @Test public void test_fft_one_dim_matrixBlock() { - MatrixBlock re = new MatrixBlock(1, 4, new double[] {0, 18, -15, 3}); - MatrixBlock im = new MatrixBlock(1, 4, new double[] {0, 0, 0, 0}); + MatrixBlock re = new MatrixBlock(1, 4, new double[]{0, 18, -15, 3}); + MatrixBlock im = new MatrixBlock(1, 4, new double[]{0, 0, 0, 0}); double[] expected_re = {6, 15, -36, 15}; double[] expected_im = {0, -15, 0, 15}; @@ -79,8 +79,8 @@ public void test_fft_one_dim_matrixBlock() { @Test public void test_ifft_one_dim_matrixBlock_2() { - double[] in_re = new double[] {1, -2, 3, -4}; - double[] in_im = new double[] {0, 0, 0, 0}; + double[] in_re = new double[]{1, -2, 3, -4}; + double[] in_im = new double[]{0, 0, 0, 0}; MatrixBlock re = new MatrixBlock(1, 4, in_re); MatrixBlock im = new MatrixBlock(1, 4, in_im); @@ -96,10 +96,10 @@ public void test_ifft_one_dim_matrixBlock_2() { @Test public void test_fft_two_dim_matrixBlock() { - MatrixBlock re = new MatrixBlock(2, 2, new double[] {0, 18, -15, 3}); - MatrixBlock im = new MatrixBlock(2, 2, new double[] {0, 0, 0, 0}); + MatrixBlock re = new MatrixBlock(2, 2, new double[]{0, 18, -15, 3}); + MatrixBlock im = new MatrixBlock(2, 2, new double[]{0, 0, 0, 0}); - double[] expected_re = {6, -36, 30, 0}; + double[] expected_re = {6,-36, 30, 0}; double[] expected_im = {0, 0, 0, 0}; MatrixBlock[] res = fft(re, im); @@ -112,8 +112,8 @@ public void test_fft_two_dim_matrixBlock() { @Test public void test_ifft_two_dim_matrixBlock() { - MatrixBlock re = new MatrixBlock(2, 2, new double[] {6, -36, 30, 0}); - MatrixBlock im = new MatrixBlock(2, 2, new double[] {0, 0, 0, 0}); + MatrixBlock re = new MatrixBlock(2, 2, new double[]{6,-36, 30, 0}); + MatrixBlock im = new MatrixBlock(2, 2, new double[]{0, 0, 0, 0}); double[] expected_re = {0, 18, -15, 3}; double[] expected_im = {0, 0, 0, 0}; @@ -128,8 +128,8 @@ public void test_ifft_two_dim_matrixBlock() { @Test public void test_fft_two_dim_matrixBlock_row_1() { - MatrixBlock re = new MatrixBlock(1, 2, new double[] {0, 18}); - MatrixBlock im = new MatrixBlock(1, 2, new double[] {0, 0}); + MatrixBlock re = new MatrixBlock(1, 2, new double[]{0, 18}); + MatrixBlock im = new MatrixBlock(1, 2, new double[]{0, 0}); double[] expected_re = {18, -18}; double[] expected_im = {0, 0}; @@ -144,8 +144,8 @@ public void test_fft_two_dim_matrixBlock_row_1() { @Test public void test_fft_two_dim_matrixBlock_row_2() { - MatrixBlock re = new MatrixBlock(1, 2, new double[] {-15, 3}); - MatrixBlock im = new MatrixBlock(1, 2, new double[] {0, 0}); + MatrixBlock re = new MatrixBlock(1, 2, new double[]{ -15, 3}); + MatrixBlock im = new MatrixBlock(1, 2, new double[]{0, 0}); double[] expected_re = {-12, -18}; double[] expected_im = {0, 0}; @@ -161,23 +161,28 @@ public void test_fft_two_dim_matrixBlock_row_2() { public void test_ifft_with_complex_numpy_data() { // removed 0's at the end, not just real - MatrixBlock re = new MatrixBlock(1, 16, - new double[] {0.5398705320215192, 0.1793355360736929, 0.9065254044489506, 0.45004385530909075, - 0.11128090341119468, 0.11742862805303522, 0.7574827475752481, 0.2778193170985158, 0.9251562928110273, - 0.9414429667551927, 0.45131569795507087, 0.9522067687409731, 0.22491032260636257, 0.6579426733967295, - 0.7021558730366062, 0.7861117825617701,}); - - MatrixBlock im = new MatrixBlock(1, 16, new double[16]); - - double[] expected_re = {0.5613143313659362, -0.020146500453061336, 0.07086545895481336, -0.05003801442765281, - -0.06351635451036074, -0.03346768844048936, 0.07023899089706032, 0.007330763123826495, 0.016022890367311193, - 0.007330763123826495, 0.07023899089706032, -0.03346768844048936, -0.06351635451036074, -0.05003801442765281, - 0.07086545895481336, -0.020146500453061336}; + MatrixBlock re = new MatrixBlock(1, 16, new double[]{ + 0.5398705320215192, 0.1793355360736929, 0.9065254044489506, 0.45004385530909075, + 0.11128090341119468, 0.11742862805303522, 0.7574827475752481, 0.2778193170985158, + 0.9251562928110273, 0.9414429667551927, 0.45131569795507087, 0.9522067687409731, + 0.22491032260636257, 0.6579426733967295, 0.7021558730366062, 0.7861117825617701, + }); + + MatrixBlock im = new MatrixBlock(1, 16, new double[16]); + + double[] expected_re = { + 0.5613143313659362, -0.020146500453061336, 0.07086545895481336, -0.05003801442765281, + -0.06351635451036074, -0.03346768844048936, 0.07023899089706032, 0.007330763123826495, + 0.016022890367311193, 0.007330763123826495, 0.07023899089706032, -0.03346768844048936, + -0.06351635451036074, -0.05003801442765281, 0.07086545895481336, -0.020146500453061336 + }; - double[] expected_im = {0.0, -0.07513090216687965, 0.023854392878864396, -0.018752997939582024, - -0.035626994964481226, -0.07808216015046443, 0.036579082654843526, -0.10605270957897009, 0.0, - 0.10605270957897009, -0.036579082654843526, 0.07808216015046443, 0.035626994964481226, 0.018752997939582024, - -0.023854392878864396, 0.07513090216687965}; + double[] expected_im = { + 0.0, -0.07513090216687965, 0.023854392878864396, -0.018752997939582024, + -0.035626994964481226, -0.07808216015046443, 0.036579082654843526, -0.10605270957897009, + 0.0, 0.10605270957897009, -0.036579082654843526, 0.07808216015046443, + 0.035626994964481226, 0.018752997939582024, -0.023854392878864396, 0.07513090216687965 + }; MatrixBlock[] res = ifft(re, im); @@ -189,16 +194,22 @@ public void test_ifft_with_complex_numpy_data() { @Test public void test_ifft_2d_with_generated_data() { - MatrixBlock re = new MatrixBlock(2, 2, - new double[] {0.6749989259154331, 0.6278845555308362, 0.995916990652601, 0.511472971081564}); + MatrixBlock re = new MatrixBlock(2, 2, new double[]{ + 0.6749989259154331, 0.6278845555308362, 0.995916990652601, 0.511472971081564 + }); - MatrixBlock im = new MatrixBlock(2, 2, - new double[] {0.8330832079105173, 0.09857986129294982, 0.6808883894146879, 0.28353782431047303}); + MatrixBlock im = new MatrixBlock(2, 2, new double[]{ + 0.8330832079105173, 0.09857986129294982, 0.6808883894146879, 0.28353782431047303 + }); // adjusted the expected output - double[] expected_re = {0.70256836, 0.1328896, -0.05112662, -0.10933241}; + double[] expected_re = { + 0.70256836, 0.1328896, -0.05112662, -0.10933241 + }; - double[] expected_im = {0.47402232, 0.28296348, -0.00819079, 0.0842882}; + double[] expected_im = { + 0.47402232, 0.28296348, -0.00819079, 0.0842882 + }; MatrixBlock[] res = ifft(re, im); @@ -211,21 +222,28 @@ public void test_ifft_2d_with_generated_data() { public void test_ifft_with_real_numpy_data() { // not complex - MatrixBlock re = new MatrixBlock(1, 16, - new double[] {0.17768499045697306, 0.3405035491673728, 0.9272906450602005, 0.28247504052271843, - 0.42775517613102865, 0.8338783039818357, 0.9813749624557385, 0.47224489612381737, 0.7936831995784907, - 0.5584182145651306, 0.5296113722056018, 0.6687593295928902, 0.9630598447622862, 0.7130539473424196, - 0.860081483892192, 0.8985058305053549}); + MatrixBlock re = new MatrixBlock(1, 16, new double[]{ + 0.17768499045697306, 0.3405035491673728, 0.9272906450602005, 0.28247504052271843, + 0.42775517613102865, 0.8338783039818357, 0.9813749624557385, 0.47224489612381737, + 0.7936831995784907, 0.5584182145651306, 0.5296113722056018, 0.6687593295928902, + 0.9630598447622862, 0.7130539473424196, 0.860081483892192, 0.8985058305053549 + }); - MatrixBlock im = new MatrixBlock(1, 16, new double[16]); + MatrixBlock im = new MatrixBlock(1, 16, new double[16]); // adjusted the expected output - double[] expected_re = {0.6517738, -0.0263837, -0.03631354, -0.01644966, -0.05851095, -0.0849794, -0.01611732, - -0.02618679, 0.05579391, -0.02618679, -0.01611732, -0.0849794, -0.05851095, -0.01644966, -0.03631354, - -0.0263837}; + double[] expected_re = { + 0.6517738, -0.0263837 , -0.03631354, -0.01644966, + -0.05851095, -0.0849794, -0.01611732, -0.02618679, + 0.05579391, -0.02618679, -0.01611732, -0.0849794, + -0.05851095, -0.01644966, -0.03631354, -0.0263837 + }; - double[] expected_im = {0, -0.04125649, -0.07121312, 0.02554502, 0.00774181, -0.08723921, -0.02314382, - -0.02021455, 0, 0.02021455, 0.02314382, 0.08723921, -0.00774181, -0.02554502, 0.07121312, 0.04125649 + double[] expected_im = { + 0, -0.04125649, -0.07121312, 0.02554502, + 0.00774181, -0.08723921, -0.02314382, -0.02021455, + 0, 0.02021455, 0.02314382, 0.08723921, + -0.00774181, -0.02554502, 0.07121312, 0.04125649 }; @@ -239,64 +257,81 @@ public void test_ifft_with_real_numpy_data() { @Test public void test_fft_two_dim_8_times_8() { - MatrixBlock re = new MatrixBlock(8, 8, - new double[] {0.8435874964408077, 0.3565209485970835, 0.6221038572251737, 0.05712418097055716, - 0.9301368966310067, 0.7748052735242277, 0.21117129518682443, 0.08407931152930459, 0.5861235649815163, - 0.45860122035396356, 0.6647476180103304, 0.9167930424492593, 0.6310726270028377, 0.11110504251770592, - 0.32369996452324756, 0.5790548902504138, 0.5712310851880162, 0.5967356161025353, 0.6441861776319489, - 0.14402445187596158, 0.22642623625293545, 0.922443731897705, 0.9527667119829785, 0.2250880965427453, - 0.5755375055168817, 0.48898427237526954, 0.24518238824389693, 0.832292384016089, 0.23789083930394805, - 0.5558982102157535, 0.7220016080026206, 0.9666747522359772, 0.20509423975210916, 0.23170117015755587, - 0.7141206714718693, 0.2014158450611332, 0.6486924358372994, 0.9044990419216931, 0.19849364935627056, - 0.23340297110822106, 0.46854050631969246, 0.10134155509558795, 0.5563200388698989, 0.2669820016661475, - 0.8889445005077763, 0.4273462470993935, 0.8269490075576963, 0.044351336481537995, 0.3771564738915597, - 0.11333723996854606, 0.6913138435759023, 0.062431275099310124, 0.8003013976959878, 0.1276686539064662, - 0.975167392001707, 0.44595301043682656, 0.18401328301977316, 0.7158585484384759, 0.3240126702723025, - 0.740836665073052, 0.8890279623888511, 0.8841266040978419, 0.3058930798936259, 0.8987579873722049}); - - MatrixBlock im = new MatrixBlock(1, 16, - new double[] {0.8572457113722648, 0.668182795310341, 0.9739416721141464, 0.8189153345383146, - 0.6425950286263254, 0.3569634253534639, 0.19715070300424575, 0.8915344479242211, 0.39207930659031054, - 0.1625193685179268, 0.2523438052868171, 0.30940628850519547, 0.7461468672112159, 0.7123766750132684, - 0.5261041429273977, 0.867155304805022, 0.7207769261821749, 0.9139070611733158, 0.7638265842242135, - 0.3508092733308539, 0.6075639148195967, 0.9615531048215422, 0.719499617407839, 0.9616615941848492, - 0.2667126256574347, 0.8215093145949468, 0.4240476512138287, 0.5015798652459079, 0.19784651066995873, - 0.42315603332105356, 0.5575575283922164, 0.9051304828282485, 0.30117855478511435, 0.14219967492505514, - 0.32675429179906557, 0.04889894374947912, 0.8338579676700041, 0.370201089804747, 0.06025987717830994, - 0.9407970353033787, 0.9871788482561391, 0.75984297199074, 0.414969247979073, 0.2453785474698862, - 0.06295683447294731, 0.40141192931768566, 0.19520663793867488, 0.3179027928938928, 0.591138083168947, - 0.5318366162549014, 0.04865894304644136, 0.5339043989658795, 0.09892519435896363, 0.31616794516128466, - 0.06702286400447643, 0.8466767273121609, 0.8134875055724791, 0.6232554321597641, 0.21208039111457444, - 0.25629831822305926, 0.7373140896724466, 0.020486629088602437, 0.8666668269441752, - 0.20094387974200512}); - - double[] expected_re = {32.51214260297584, -3.4732779237490314, -0.7257760912890102, -1.9627494786611792, - 3.571671446098747, 1.0451692206901078, 0.8970702451384204, -1.3739767803210428, 4.892442103095981, - -1.1656855109832338, -0.5854908742291178, -1.3497699084098418, -1.377003693155216, -2.1698030461214923, - 0.8172129683973663, -0.9259076518379679, -1.1343756245445045, -1.8734967800709579, 1.7367517585478862, - 0.07349671655414491, -1.5933768052439223, 2.7965196291943983, 4.292588673604611, -1.1032899622026413, - -2.4643093702874834, 2.109128987930992, 3.2834030498896456, 0.21371926254596152, -0.3107488550365316, - 0.7293395030253796, -2.542403789759091, -1.8570654162590052, -2.325781245331303, 0.7963395911053484, - -2.351990667205867, -2.4241188304485735, 4.689766636746301, 3.4748121457306116, 0.5539071663846459, - -0.950099313504134, 2.122310975524349, 4.527637759721644, -2.125596093625001, 1.7676565539001592, - 5.748332643019926, 0.860140632830907, 2.9735142186218484, -1.7198774815194848, -0.18418859401548549, - 1.1909629561342188, 0.21710627714418418, -1.5537184277268996, -0.5486540814747869, 0.14807346060743987, - 2.4333154010438087, -3.2930077637380393, -2.3820067665775113, 2.5463581304688057, 2.5927580716559615, - 1.8921802492721915, 0.4957713559465988, -0.4983536537999108, 3.5808175362367676, 0.7530823235547575}; - - double[] expected_im = {32.64565805549281, 5.177639365468945, 1.1792020104097647, -0.4850627423320939, - -1.719468548169175, -3.064146170894837, 3.3226243586118906, 2.3819341640916107, 1.5824429804361388, - -2.192940882164737, 0.5774407122593543, 0.16873948200103983, 4.297014293326352, -3.1712082122026883, - 0.9741291131305898, -2.4929883795121235, 1.111763301820595, -1.4012254390671657, -0.33687898317382636, - 2.324190267133635, -2.8862969254091397, -4.7558982401265135, 1.8244587481290004, 0.5310550630270396, - 2.655726742689745, 2.510014260306531, 0.25589537824783704, 1.8720307201415736, -2.6458046644482884, - 2.1732611302115585, -2.5162250969793227, -0.9103444457919911, 2.2835527482590248, 0.5187392677625127, - -3.335253420903965, 1.4668560670097441, -1.9681585205341436, -2.81914578771063, 4.818094364700921, - -0.877636803126361, 1.803174743823159, 3.1346192487664277, -3.564058675191744, -0.3391381913837902, - -1.2897867384105863, 2.315065426377637, 1.5764817121472765, 2.412894091248795, -2.3182678917385218, - -3.057303547366563, 0.033996764974414395, -0.5825423640666696, 6.395088232674363, -0.5553659624089358, - 1.1079219041153268, 0.1094531803830765, 3.488182265163636, -1.5698242466218544, -0.1013387045518459, - 0.9269290699615746, -0.699890233104248, 3.617209720991753, -0.5565163478425035, 3.502962737763559}; + MatrixBlock re = new MatrixBlock(8, 8, new double[]{ + 0.8435874964408077, 0.3565209485970835, 0.6221038572251737, 0.05712418097055716, + 0.9301368966310067, 0.7748052735242277, 0.21117129518682443, 0.08407931152930459, + 0.5861235649815163, 0.45860122035396356, 0.6647476180103304, 0.9167930424492593, + 0.6310726270028377, 0.11110504251770592, 0.32369996452324756, 0.5790548902504138, + 0.5712310851880162, 0.5967356161025353, 0.6441861776319489, 0.14402445187596158, + 0.22642623625293545, 0.922443731897705, 0.9527667119829785, 0.2250880965427453, + 0.5755375055168817, 0.48898427237526954, 0.24518238824389693, 0.832292384016089, + 0.23789083930394805, 0.5558982102157535, 0.7220016080026206, 0.9666747522359772, + 0.20509423975210916, 0.23170117015755587, 0.7141206714718693, 0.2014158450611332, + 0.6486924358372994, 0.9044990419216931, 0.19849364935627056, 0.23340297110822106, + 0.46854050631969246, 0.10134155509558795, 0.5563200388698989, 0.2669820016661475, + 0.8889445005077763, 0.4273462470993935, 0.8269490075576963, 0.044351336481537995, + 0.3771564738915597, 0.11333723996854606, 0.6913138435759023, 0.062431275099310124, + 0.8003013976959878, 0.1276686539064662, 0.975167392001707, 0.44595301043682656, + 0.18401328301977316, 0.7158585484384759, 0.3240126702723025, 0.740836665073052, + 0.8890279623888511, 0.8841266040978419, 0.3058930798936259, 0.8987579873722049 + }); + + MatrixBlock im = new MatrixBlock(1, 16, new double[]{ + 0.8572457113722648, 0.668182795310341, 0.9739416721141464, 0.8189153345383146, + 0.6425950286263254, 0.3569634253534639, 0.19715070300424575, 0.8915344479242211, + 0.39207930659031054, 0.1625193685179268, 0.2523438052868171, 0.30940628850519547, + 0.7461468672112159, 0.7123766750132684, 0.5261041429273977, 0.867155304805022, + 0.7207769261821749, 0.9139070611733158, 0.7638265842242135, 0.3508092733308539, + 0.6075639148195967, 0.9615531048215422, 0.719499617407839, 0.9616615941848492, + 0.2667126256574347, 0.8215093145949468, 0.4240476512138287, 0.5015798652459079, + 0.19784651066995873, 0.42315603332105356, 0.5575575283922164, 0.9051304828282485, + 0.30117855478511435, 0.14219967492505514, 0.32675429179906557, 0.04889894374947912, + 0.8338579676700041, 0.370201089804747, 0.06025987717830994, 0.9407970353033787, + 0.9871788482561391, 0.75984297199074, 0.414969247979073, 0.2453785474698862, + 0.06295683447294731, 0.40141192931768566, 0.19520663793867488, 0.3179027928938928, + 0.591138083168947, 0.5318366162549014, 0.04865894304644136, 0.5339043989658795, + 0.09892519435896363, 0.31616794516128466, 0.06702286400447643, 0.8466767273121609, + 0.8134875055724791, 0.6232554321597641, 0.21208039111457444, 0.25629831822305926, + 0.7373140896724466, 0.020486629088602437, 0.8666668269441752, 0.20094387974200512 + }); + + double[] expected_re = { + 32.51214260297584, -3.4732779237490314, -0.7257760912890102, -1.9627494786611792, + 3.571671446098747, 1.0451692206901078, 0.8970702451384204, -1.3739767803210428, + 4.892442103095981, -1.1656855109832338, -0.5854908742291178, -1.3497699084098418, + -1.377003693155216, -2.1698030461214923, 0.8172129683973663, -0.9259076518379679, + -1.1343756245445045, -1.8734967800709579, 1.7367517585478862, 0.07349671655414491, + -1.5933768052439223, 2.7965196291943983, 4.292588673604611, -1.1032899622026413, + -2.4643093702874834, 2.109128987930992, 3.2834030498896456, 0.21371926254596152, + -0.3107488550365316, 0.7293395030253796, -2.542403789759091, -1.8570654162590052, + -2.325781245331303, 0.7963395911053484, -2.351990667205867, -2.4241188304485735, + 4.689766636746301, 3.4748121457306116, 0.5539071663846459, -0.950099313504134, + 2.122310975524349, 4.527637759721644, -2.125596093625001, 1.7676565539001592, + 5.748332643019926, 0.860140632830907, 2.9735142186218484, -1.7198774815194848, + -0.18418859401548549, 1.1909629561342188, 0.21710627714418418, -1.5537184277268996, + -0.5486540814747869, 0.14807346060743987, 2.4333154010438087, -3.2930077637380393, + -2.3820067665775113, 2.5463581304688057, 2.5927580716559615, 1.8921802492721915, + 0.4957713559465988, -0.4983536537999108, 3.5808175362367676, 0.7530823235547575 + }; + + double[] expected_im = { + 32.64565805549281, 5.177639365468945, 1.1792020104097647, -0.4850627423320939, + -1.719468548169175, -3.064146170894837, 3.3226243586118906, 2.3819341640916107, + 1.5824429804361388, -2.192940882164737, 0.5774407122593543, 0.16873948200103983, + 4.297014293326352, -3.1712082122026883, 0.9741291131305898, -2.4929883795121235, + 1.111763301820595, -1.4012254390671657, -0.33687898317382636, 2.324190267133635, + -2.8862969254091397, -4.7558982401265135, 1.8244587481290004, 0.5310550630270396, + 2.655726742689745, 2.510014260306531, 0.25589537824783704, 1.8720307201415736, + -2.6458046644482884, 2.1732611302115585, -2.5162250969793227, -0.9103444457919911, + 2.2835527482590248, 0.5187392677625127, -3.335253420903965, 1.4668560670097441, + -1.9681585205341436, -2.81914578771063, 4.818094364700921, -0.877636803126361, + 1.803174743823159, 3.1346192487664277, -3.564058675191744, -0.3391381913837902, + -1.2897867384105863, 2.315065426377637, 1.5764817121472765, 2.412894091248795, + -2.3182678917385218, -3.057303547366563, 0.033996764974414395, -0.5825423640666696, + 6.395088232674363, -0.5553659624089358, 1.1079219041153268, 0.1094531803830765, + 3.488182265163636, -1.5698242466218544, -0.1013387045518459, 0.9269290699615746, + -0.699890233104248, 3.617209720991753, -0.5565163478425035, 3.502962737763559 + }; MatrixBlock[] res = fft(re, im); From 966703135129fc25f894a2e2a1cab99b9899fe66 Mon Sep 17 00:00:00 2001 From: Frederic Zoepffel Date: Thu, 8 Feb 2024 23:03:21 +0100 Subject: [PATCH 053/133] reverse formatting in LibCommonsMath --- .../runtime/matrix/data/LibCommonsMath.java | 236 +++++++++--------- 1 file changed, 113 insertions(+), 123 deletions(-) diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java index 924477100d7..bca9adbd010 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java @@ -53,26 +53,27 @@ import static org.apache.sysds.runtime.matrix.data.LibMatrixFourier.ifft; /** - * Library for matrix operations that need invocation of - * Apache Commons Math library. + * Library for matrix operations that need invocation of + * Apache Commons Math library. * * This library currently supports following operations: - * matrix inverse, matrix decompositions (QR, LU, Eigen), solve + * matrix inverse, matrix decompositions (QR, LU, Eigen), solve */ -public class LibCommonsMath { +public class LibCommonsMath +{ private static final Log LOG = LogFactory.getLog(LibCommonsMath.class.getName()); private static final double RELATIVE_SYMMETRY_THRESHOLD = 1e-6; private static final double EIGEN_LAMBDA = 1e-8; private LibCommonsMath() { - // prevent instantiation via private constructor + //prevent instantiation via private constructor } - - public static boolean isSupportedUnaryOperation(String opcode) { - return (opcode.equals("inverse") || opcode.equals("cholesky")); + + public static boolean isSupportedUnaryOperation( String opcode ) { + return ( opcode.equals("inverse") || opcode.equals("cholesky") ); } - - public static boolean isSupportedMultiReturnOperation(String opcode) { + + public static boolean isSupportedMultiReturnOperation( String opcode ) { switch (opcode) { case "qr": @@ -80,21 +81,19 @@ public static boolean isSupportedMultiReturnOperation(String opcode) { case "eigen": case "fft": case "ifft": - case "svd": - return true; - default: - return false; + case "svd": return true; + default: return false; } } - - public static boolean isSupportedMatrixMatrixOperation(String opcode) { - return (opcode.equals("solve")); + + public static boolean isSupportedMatrixMatrixOperation( String opcode ) { + return ( opcode.equals("solve") ); } - + public static MatrixBlock unaryOperations(MatrixBlock inj, String opcode) { Array2DRowRealMatrix matrixInput = DataConverter.convertToArray2DRowRealMatrix(inj); - if (opcode.equals("inverse")) + if(opcode.equals("inverse")) return computeMatrixInverse(matrixInput); else if (opcode.equals("cholesky")) return computeCholesky(matrixInput); @@ -174,26 +173,24 @@ public static MatrixBlock matrixMatrixOperations(MatrixBlock in1, MatrixBlock in * @return matrix block */ private static MatrixBlock computeSolve(MatrixBlock in1, MatrixBlock in2) { - // convert to commons math BlockRealMatrix instead of Array2DRowRealMatrix - // to avoid unnecessary conversion as QR internally creates a BlockRealMatrix + //convert to commons math BlockRealMatrix instead of Array2DRowRealMatrix + //to avoid unnecessary conversion as QR internally creates a BlockRealMatrix BlockRealMatrix matrixInput = DataConverter.convertToBlockRealMatrix(in1); BlockRealMatrix vectorInput = DataConverter.convertToBlockRealMatrix(in2); - - /* - * LUDecompositionImpl ludecompose = new LUDecompositionImpl(matrixInput); - * DecompositionSolver lusolver = ludecompose.getSolver(); - * RealMatrix solutionMatrix = lusolver.solve(vectorInput); - */ - + + /*LUDecompositionImpl ludecompose = new LUDecompositionImpl(matrixInput); + DecompositionSolver lusolver = ludecompose.getSolver(); + RealMatrix solutionMatrix = lusolver.solve(vectorInput);*/ + // Setup a solver based on QR Decomposition QRDecomposition qrdecompose = new QRDecomposition(matrixInput); DecompositionSolver solver = qrdecompose.getSolver(); // Invoke solve RealMatrix solutionMatrix = solver.solve(vectorInput); - + return DataConverter.convertToMatrixBlock(solutionMatrix); } - + /** * Function to perform QR decomposition on a given matrix. * @@ -202,19 +199,19 @@ private static MatrixBlock computeSolve(MatrixBlock in1, MatrixBlock in2) { */ private static MatrixBlock[] computeQR(MatrixBlock in) { Array2DRowRealMatrix matrixInput = DataConverter.convertToArray2DRowRealMatrix(in); - + // Perform QR decomposition QRDecomposition qrdecompose = new QRDecomposition(matrixInput); RealMatrix H = qrdecompose.getH(); RealMatrix R = qrdecompose.getR(); - + // Read the results into native format MatrixBlock mbH = DataConverter.convertToMatrixBlock(H.getData()); MatrixBlock mbR = DataConverter.convertToMatrixBlock(R.getData()); return new MatrixBlock[] { mbH, mbR }; } - + /** * Function to perform LU decomposition on a given matrix. * @@ -222,20 +219,20 @@ private static MatrixBlock[] computeQR(MatrixBlock in) { * @return array of matrix blocks */ private static MatrixBlock[] computeLU(MatrixBlock in) { - if (in.getNumRows() != in.getNumColumns()) { + if(in.getNumRows() != in.getNumColumns()) { throw new DMLRuntimeException( - "LU Decomposition can only be done on a square matrix. Input matrix is rectangular (rows=" - + in.getNumRows() + ", cols=" + in.getNumColumns() + ")"); + "LU Decomposition can only be done on a square matrix. Input matrix is rectangular (rows=" + + in.getNumRows() + ", cols=" + in.getNumColumns() + ")"); } - + Array2DRowRealMatrix matrixInput = DataConverter.convertToArray2DRowRealMatrix(in); - + // Perform LUP decomposition LUDecomposition ludecompose = new LUDecomposition(matrixInput); RealMatrix P = ludecompose.getP(); RealMatrix L = ludecompose.getL(); RealMatrix U = ludecompose.getU(); - + // Read the results into native format MatrixBlock mbP = DataConverter.convertToMatrixBlock(P.getData()); MatrixBlock mbL = DataConverter.convertToMatrixBlock(L.getData()); @@ -243,7 +240,7 @@ private static MatrixBlock[] computeLU(MatrixBlock in) { return new MatrixBlock[] { mbP, mbL, mbU }; } - + /** * Function to perform Eigen decomposition on a given matrix. * Input must be a symmetric matrix. @@ -252,20 +249,21 @@ private static MatrixBlock[] computeLU(MatrixBlock in) { * @return array of matrix blocks */ private static MatrixBlock[] computeEigen(MatrixBlock in) { - if (in.getNumRows() != in.getNumColumns()) { + if ( in.getNumRows() != in.getNumColumns() ) { throw new DMLRuntimeException("Eigen Decomposition can only be done on a square matrix. " - + "Input matrix is rectangular (rows=" + in.getNumRows() + ", cols=" + in.getNumColumns() + ")"); + + "Input matrix is rectangular (rows=" + in.getNumRows() + ", cols="+ in.getNumColumns() +")"); } - + EigenDecomposition eigendecompose = null; try { Array2DRowRealMatrix matrixInput = DataConverter.convertToArray2DRowRealMatrix(in); eigendecompose = new EigenDecomposition(matrixInput); - } catch (MaxCountExceededException ex) { - LOG.warn("Eigen: " + ex.getMessage() + ". Falling back to regularized eigen factorization."); + } + catch(MaxCountExceededException ex) { + LOG.warn("Eigen: "+ ex.getMessage()+". Falling back to regularized eigen factorization."); eigendecompose = computeEigenRegularized(in); } - + RealMatrix eVectorsMatrix = eigendecompose.getV(); double[][] eVectors = eVectorsMatrix.getData(); double[] eValues = eigendecompose.getRealEigenvalues(); @@ -274,24 +272,24 @@ private static MatrixBlock[] computeEigen(MatrixBlock in) { } private static EigenDecomposition computeEigenRegularized(MatrixBlock in) { - if (in == null || in.isEmptyBlock(false)) + if( in == null || in.isEmptyBlock(false) ) throw new DMLRuntimeException("Invalid empty block"); - - // slightly modify input for regularization (pos/neg) + + //slightly modify input for regularization (pos/neg) MatrixBlock in2 = new MatrixBlock(in, false); DenseBlock a = in2.getDenseBlock(); - for (int i = 0; i < in2.rlen; i++) { + for( int i=0; i= 1e-7) + if(Math.abs(v1.sumSq() - 1.0) >= 1e-7) throw new DMLRuntimeException("v1 not correctly normalized (maybe try changing the seed)"); return v1; } /** - * Function to perform the Lanczos algorithm and then computes the - * Eigendecomposition. - * Caution: Lanczos is not numerically stable (see - * https://en.wikipedia.org/wiki/Lanczos_algorithm) + * Function to perform the Lanczos algorithm and then computes the Eigendecomposition. + * Caution: Lanczos is not numerically stable (see https://en.wikipedia.org/wiki/Lanczos_algorithm) * Input must be a symmetric (and square) matrix. * - * @param in matrix object + * @param in matrix object * @param threads number of threads - * @param seed seed for the random MatrixBlock generation + * @param seed seed for the random MatrixBlock generation * @return array of matrix blocks */ private static MatrixBlock[] computeEigenLanczos(MatrixBlock in, int threads, long seed) { - if (in.getNumRows() != in.getNumColumns()) { + if(in.getNumRows() != in.getNumColumns()) { throw new DMLRuntimeException( - "Lanczos algorithm and Eigen Decomposition can only be done on a square matrix. " - + "Input matrix is rectangular (rows=" + in.getNumRows() + ", cols=" + in.getNumColumns() - + ")"); + "Lanczos algorithm and Eigen Decomposition can only be done on a square matrix. " + + "Input matrix is rectangular (rows=" + in.getNumRows() + ", cols=" + in.getNumColumns() + ")"); } int m = in.getNumRows(); @@ -449,12 +444,11 @@ private static MatrixBlock[] computeEigenLanczos(MatrixBlock in, int threads, lo ScalarOperator op_div_scalar = new RightScalarOperator(Divide.getDivideFnObject(), 1, threads); MatrixBlock beta = new MatrixBlock(1, 1, 0.0); - for (int i = 0; i < m; i++) { + for(int i = 0; i < m; i++) { v1.putInto(TV, 0, i, false); w1 = in.aggregateBinaryOperations(in, v1, op_mul_agg); - MatrixBlock alpha = w1.aggregateBinaryOperations(v1.reorgOperations(op_t, new MatrixBlock(), 0, 0, m), w1, - op_mul_agg); - if (i < m - 1) { + MatrixBlock alpha = w1.aggregateBinaryOperations(v1.reorgOperations(op_t, new MatrixBlock(), 0, 0, m), w1, op_mul_agg); + if(i < m - 1) { w1 = w1.ternaryOperations(op_minus_mul, v1, alpha, new MatrixBlock()); w1 = w1.ternaryOperations(op_minus_mul, v0, beta, new MatrixBlock()); beta.setValue(0, 0, Math.sqrt(w1.sumSq())); @@ -469,7 +463,7 @@ private static MatrixBlock[] computeEigenLanczos(MatrixBlock in, int threads, lo } MatrixBlock[] e = computeEigen(T); - TV.setNonZeros((long) m * m); + TV.setNonZeros((long) m*m); e[1] = TV.aggregateBinaryOperations(TV, e[1], op_mul_agg); return e; } @@ -477,17 +471,16 @@ private static MatrixBlock[] computeEigenLanczos(MatrixBlock in, int threads, lo /** * Function to perform the QR decomposition. * Input must be a square matrix. - * TODO: use Householder transformation and implicit shifts to further speed up - * QR decompositions + * TODO: use Householder transformation and implicit shifts to further speed up QR decompositions * - * @param in matrix object + * @param in matrix object * @param threads number of threads * @return array of matrix blocks [Q, R] */ private static MatrixBlock[] computeQR2(MatrixBlock in, int threads) { - if (in.getNumRows() != in.getNumColumns()) { + if(in.getNumRows() != in.getNumColumns()) { throw new DMLRuntimeException("QR2 Decomposition can only be done on a square matrix. " - + "Input matrix is rectangular (rows=" + in.getNumRows() + ", cols=" + in.getNumColumns() + ")"); + + "Input matrix is rectangular (rows=" + in.getNumRows() + ", cols=" + in.getNumColumns() + ")"); } int m = in.rlen; @@ -496,7 +489,7 @@ private static MatrixBlock[] computeQR2(MatrixBlock in, int threads) { A_n.copy(in); MatrixBlock Q_n = new MatrixBlock(m, m, true); - for (int i = 0; i < m; i++) { + for(int i = 0; i < m; i++) { Q_n.setValue(i, i, 1.0); } @@ -506,7 +499,7 @@ private static MatrixBlock[] computeQR2(MatrixBlock in, int threads) { ScalarOperator op_div_scalar = new RightScalarOperator(Divide.getDivideFnObject(), 1, threads); ScalarOperator op_mult_2 = new LeftScalarOperator(Multiply.getMultiplyFnObject(), 2, threads); - for (int k = 0; k < m; k++) { + for(int k = 0; k < m; k++) { MatrixBlock z = A_n.slice(k, m - 1, k, k); MatrixBlock uk = new MatrixBlock(m - k, 1, 0.0); uk.copy(z); @@ -525,16 +518,15 @@ private static MatrixBlock[] computeQR2(MatrixBlock in, int threads) { Q_n = Q_n.binaryOperations(op_sub, Q_n.aggregateBinaryOperations(Q_n, vkvkt2, op_mul_agg)); } // QR decomp: Q: Q_n; R: A_n - return new MatrixBlock[] { Q_n, A_n }; + return new MatrixBlock[] {Q_n, A_n}; } /** * Function that computes the Eigen Decomposition using the QR algorithm. * Caution: check if the QR algorithm is converged, if not increase iterations - * Caution: if the input matrix has complex eigenvalues results will be - * incorrect + * Caution: if the input matrix has complex eigenvalues results will be incorrect * - * @param in Input matrix + * @param in Input matrix * @param threads number of threads * @return array of matrix blocks */ @@ -543,33 +535,32 @@ private static MatrixBlock[] computeEigenQR(MatrixBlock in, int threads) { } private static MatrixBlock[] computeEigenQR(MatrixBlock in, int num_iterations, double tol, int threads) { - if (in.getNumRows() != in.getNumColumns()) { + if(in.getNumRows() != in.getNumColumns()) { throw new DMLRuntimeException("Eigen Decomposition (QR) can only be done on a square matrix. " - + "Input matrix is rectangular (rows=" + in.getNumRows() + ", cols=" + in.getNumColumns() + ")"); + + "Input matrix is rectangular (rows=" + in.getNumRows() + ", cols=" + in.getNumColumns() + ")"); } int m = in.rlen; AggregateBinaryOperator op_mul_agg = InstructionUtils.getMatMultOperator(threads); MatrixBlock Q_prod = new MatrixBlock(m, m, 0.0); - for (int i = 0; i < m; i++) { + for(int i = 0; i < m; i++) { Q_prod.setValue(i, i, 1.0); } - for (int i = 0; i < num_iterations; i++) { + for(int i = 0; i < num_iterations; i++) { MatrixBlock[] QR = computeQR2(in, threads); Q_prod = Q_prod.aggregateBinaryOperations(Q_prod, QR[0], op_mul_agg); in = QR[1].aggregateBinaryOperations(QR[1], QR[0], op_mul_agg); } - // Is converged if all values are below tol and the there only is values on the - // diagonal. + // Is converged if all values are below tol and the there only is values on the diagonal. double[] check = in.getDenseBlockValues(); double[] eval = new double[m]; - for (int i = 0; i < m; i++) - eval[i] = check[i * m + i]; - + for(int i = 0; i < m; i++) + eval[i] = check[i*m+i]; + double[] evec = Q_prod.getDenseBlockValues(); return sortEVs(eval, evec); } @@ -577,7 +568,7 @@ private static MatrixBlock[] computeEigenQR(MatrixBlock in, int num_iterations, /** * Function to compute the Householder transformation of a Matrix. * - * @param in Input Matrix + * @param in Input Matrix * @param threads number of threads * @return transformed matrix */ @@ -588,14 +579,14 @@ private static MatrixBlock computeHouseholder(MatrixBlock in, int threads) { MatrixBlock A_n = new MatrixBlock(m, m, 0.0); A_n.copy(in); - for (int k = 0; k < m - 2; k++) { + for(int k = 0; k < m - 2; k++) { MatrixBlock ajk = A_n.slice(0, m - 1, k, k); - for (int i = 0; i <= k; i++) { + for(int i = 0; i <= k; i++) { ajk.setValue(i, 0, 0.0); } double alpha = Math.sqrt(ajk.sumSq()); double ak1k = A_n.getDouble(k + 1, k); - if (ak1k > 0.0) + if(ak1k > 0.0) alpha *= -1; double r = Math.sqrt(0.5 * (alpha * alpha - ak1k * alpha)); MatrixBlock v = new MatrixBlock(m, 1, 0.0); @@ -605,7 +596,7 @@ private static MatrixBlock computeHouseholder(MatrixBlock in, int threads) { v = v.scalarOperations(op_div_scalar, new MatrixBlock()); MatrixBlock P = new MatrixBlock(m, m, 0.0); - for (int i = 0; i < m; i++) { + for(int i = 0; i < m; i++) { P.setValue(i, i, 1.0); } @@ -624,8 +615,7 @@ private static MatrixBlock computeHouseholder(MatrixBlock in, int threads) { } /** - * Sort the eigen values (and vectors) in increasing order (to be compatible w/ - * LAPACK.DSYEVR()) + * Sort the eigen values (and vectors) in increasing order (to be compatible w/ LAPACK.DSYEVR()) * * @param eValues Eigenvalues * @param eVectors Eigenvectors @@ -633,19 +623,19 @@ private static MatrixBlock computeHouseholder(MatrixBlock in, int threads) { */ private static MatrixBlock[] sortEVs(double[] eValues, double[][] eVectors) { int n = eValues.length; - for (int i = 0; i < n; i++) { + for(int i = 0; i < n; i++) { int k = i; double p = eValues[i]; - for (int j = i + 1; j < n; j++) { - if (eValues[j] < p) { + for(int j = i + 1; j < n; j++) { + if(eValues[j] < p) { k = j; p = eValues[j]; } } - if (k != i) { + if(k != i) { eValues[k] = eValues[i]; eValues[i] = p; - for (int j = 0; j < n; j++) { + for(int j = 0; j < n; j++) { p = eVectors[j][i]; eVectors[j][i] = eVectors[j][k]; eVectors[j][k] = p; @@ -655,27 +645,27 @@ private static MatrixBlock[] sortEVs(double[] eValues, double[][] eVectors) { MatrixBlock eval = DataConverter.convertToMatrixBlock(eValues, true); MatrixBlock evec = DataConverter.convertToMatrixBlock(eVectors); - return new MatrixBlock[] { eval, evec }; + return new MatrixBlock[] {eval, evec}; } private static MatrixBlock[] sortEVs(double[] eValues, double[] eVectors) { int n = eValues.length; - for (int i = 0; i < n; i++) { + for(int i = 0; i < n; i++) { int k = i; double p = eValues[i]; - for (int j = i + 1; j < n; j++) { - if (eValues[j] < p) { + for(int j = i + 1; j < n; j++) { + if(eValues[j] < p) { k = j; p = eValues[j]; } } - if (k != i) { + if(k != i) { eValues[k] = eValues[i]; eValues[i] = p; - for (int j = 0; j < n; j++) { - p = eVectors[j * n + i]; - eVectors[j * n + i] = eVectors[j * n + k]; - eVectors[j * n + k] = p; + for(int j = 0; j < n; j++) { + p = eVectors[j*n+i]; + eVectors[j*n+i] = eVectors[j*n+k]; + eVectors[j*n+k] = p; } } } @@ -683,6 +673,6 @@ private static MatrixBlock[] sortEVs(double[] eValues, double[] eVectors) { MatrixBlock eval = DataConverter.convertToMatrixBlock(eValues, true); MatrixBlock evec = new MatrixBlock(n, n, false); evec.init(eVectors, n, n); - return new MatrixBlock[] { eval, evec }; + return new MatrixBlock[] {eval, evec}; } } From bc6d618cfac411e5a10a732f50d5fca438946e88 Mon Sep 17 00:00:00 2001 From: Frederic Zoepffel Date: Thu, 8 Feb 2024 23:34:08 +0100 Subject: [PATCH 054/133] BuiltinFunctionExpression reverse formatting --- .../parser/BuiltinFunctionExpression.java | 3312 ++++++++--------- 1 file changed, 1644 insertions(+), 1668 deletions(-) diff --git a/src/main/java/org/apache/sysds/parser/BuiltinFunctionExpression.java b/src/main/java/org/apache/sysds/parser/BuiltinFunctionExpression.java index 04f0d0fc0ac..94a0452f05b 100644 --- a/src/main/java/org/apache/sysds/parser/BuiltinFunctionExpression.java +++ b/src/main/java/org/apache/sysds/parser/BuiltinFunctionExpression.java @@ -44,13 +44,12 @@ public class BuiltinFunctionExpression extends DataIdentifier { protected Expression[] _args = null; private Builtins _opcode; - public BuiltinFunctionExpression(ParserRuleContext ctx, Builtins bifop, ArrayList args, - String fname) { + public BuiltinFunctionExpression(ParserRuleContext ctx, Builtins bifop, ArrayList args, String fname) { _opcode = bifop; setCtxValuesAndFilename(ctx, fname); args = expandDnnArguments(args); _args = new Expression[args.size()]; - for (int i = 0; i < args.size(); i++) { + for(int i=0; i < args.size(); i++) { _args[i] = args.get(i).getExpr(); } } @@ -67,7 +66,7 @@ public BuiltinFunctionExpression(Builtins bifop, Expression[] args, ParseInfo pa public BuiltinFunctionExpression(ParserRuleContext ctx, Builtins bifop, Expression[] args, String fname) { _opcode = bifop; _args = new Expression[args.length]; - for (int i = 0; i < args.length; i++) { + for(int i=0; i < args.length; i++) { _args[i] = args[i]; } setCtxValuesAndFilename(ctx, fname); @@ -98,19 +97,19 @@ public Expression getSecondExpr() { public Expression getThirdExpr() { return (_args.length >= 3 ? _args[2] : null); } - + public Expression getFourthExpr() { return (_args.length >= 4 ? _args[3] : null); } - + public Expression getFifthExpr() { return (_args.length >= 5 ? _args[4] : null); } - + public Expression getSixthExpr() { return (_args.length >= 6 ? _args[5] : null); } - + public Expression getSeventhExpr() { return (_args.length >= 7 ? _args[6] : null); } @@ -119,26 +118,27 @@ public Expression getEighthExpr() { return (_args.length >= 8 ? _args[7] : null); } - public Expression[] getAllExpr() { + + public Expression[] getAllExpr(){ return _args; } - + public Expression getExpr(int i) { return (_args.length > i ? _args[i] : null); } - + @Override - public void validateExpression(MultiAssignmentStatement stmt, HashMap ids, - HashMap constVars, boolean conditional) { - if (this.getFirstExpr() instanceof FunctionCallIdentifier) { + public void validateExpression(MultiAssignmentStatement stmt, HashMap ids, HashMap constVars, boolean conditional) + { + if (this.getFirstExpr() instanceof FunctionCallIdentifier){ raiseValidateError("UDF function call not supported as parameter to built-in function call", false); } - + this.getFirstExpr().validateExpression(ids, constVars, conditional); - Expression[] expr = getAllExpr(); - if (expr != null && expr.length > 1) { - for (int i = 1; i < expr.length; i++) { - if (expr[i] instanceof FunctionCallIdentifier) { + Expression [] expr = getAllExpr(); + if(expr != null && expr.length > 1) { + for(int i = 1; i < expr.length; i++) { + if (expr[i] instanceof FunctionCallIdentifier){ raiseValidateError("UDF function call not supported as parameter to built-in function call", false); } expr[i].validateExpression(ids, constVars, conditional); @@ -146,395 +146,391 @@ public void validateExpression(MultiAssignmentStatement stmt, HashMap 0 ? + getFirstExpr().getOutput().getDim1() + 1 : -1; + out1.setDataType(DataType.LIST); + out1.setValueType(getFirstExpr().getOutput().getValueType()); + out1.setDimensions(nrow, 1); + out1.setBlocksize(getFirstExpr().getOutput().getBlocksize()); + + // Output2 - list of removed element + out2.setDataType(DataType.LIST); + out2.setValueType(getFirstExpr().getOutput().getValueType()); + out2.setDimensions(1, 1); + out2.setBlocksize(getFirstExpr().getOutput().getBlocksize()); + + break; + } + case SVD: + checkNumParameters(1); + checkMatrixParam(getFirstExpr()); + + long minMN = Math.min(getFirstExpr().getOutput().getDim1(), getFirstExpr().getOutput().getDim2()); + + // setup output properties + DataIdentifier svdOut1 = (DataIdentifier) getOutputs()[0]; + DataIdentifier svdOut2 = (DataIdentifier) getOutputs()[1]; + DataIdentifier svdOut3 = (DataIdentifier) getOutputs()[2]; + + // Output 1 + svdOut1.setDataType(DataType.MATRIX); + svdOut1.setValueType(ValueType.FP64); + svdOut1.setDimensions(getFirstExpr().getOutput().getDim1(), minMN); + svdOut1.setBlocksize(getFirstExpr().getOutput().getBlocksize()); + + // Output 2 + svdOut2.setDataType(DataType.MATRIX); + svdOut2.setValueType(ValueType.FP64); + svdOut2.setDimensions(minMN, minMN); + svdOut2.setBlocksize(getFirstExpr().getOutput().getBlocksize()); + + // Output 3 + svdOut3.setDataType(DataType.MATRIX); + svdOut3.setValueType(ValueType.FP64); + svdOut3.setDimensions(getFirstExpr().getOutput().getDim2(), minMN); + svdOut3.setBlocksize(getFirstExpr().getOutput().getBlocksize()); + + break; + + case COMPRESS: + if(OptimizerUtils.ALLOW_SCRIPT_LEVEL_COMPRESS_COMMAND) { Expression expressionTwo = getSecondExpr(); checkNumParameters(getSecondExpr() != null ? 2 : 1); - checkMatrixParam(getFirstExpr()); - if (expressionTwo != null) + checkMatrixFrameParam(getFirstExpr()); + if(expressionTwo != null) checkMatrixParam(getSecondExpr()); - // setup output properties - DataIdentifier ifftOut1 = (DataIdentifier) getOutputs()[0]; - DataIdentifier ifftOut2 = (DataIdentifier) getOutputs()[1]; - - // TODO: Add Validation - // if (getFirstExpr().getOutput().getDim2() != 1 || - // getFirstExpr().getOutput().getDim2() != 2) { - // raiseValidateError("Eigen Decomposition can only be done on a square matrix. - // Input matrix is rectangular (rows=" + getFirstExpr().getOutput().getDim1() + - // ", cols="+ getFirstExpr().getOutput().getDim2() +")", conditional); - // } - - // Output1 - ifft Values - ifftOut1.setDataType(DataType.MATRIX); - ifftOut1.setValueType(ValueType.FP64); - ifftOut1.setDimensions(getFirstExpr().getOutput().getDim1(), getFirstExpr().getOutput().getDim2()); - ifftOut1.setBlocksize(getFirstExpr().getOutput().getBlocksize()); - - // Output2 - ifft Vectors - ifftOut2.setDataType(DataType.MATRIX); - ifftOut2.setValueType(ValueType.FP64); - ifftOut2.setDimensions(getFirstExpr().getOutput().getDim1(), getFirstExpr().getOutput().getDim2()); - ifftOut2.setBlocksize(getFirstExpr().getOutput().getBlocksize()); + Identifier compressInput1 = getFirstExpr().getOutput(); + // Identifier compressInput2 = getSecondExpr().getOutput(); - break; + DataIdentifier compressOutput = (DataIdentifier) getOutputs()[0]; + compressOutput.setDataType(DataType.MATRIX); + compressOutput.setDimensions(compressInput1.getDim1(), compressInput1.getDim2()); + compressOutput.setBlocksize(compressInput1.getBlocksize()); + compressOutput.setValueType(compressInput1.getValueType()); + DataIdentifier metaOutput = (DataIdentifier) getOutputs()[1]; + metaOutput.setDataType(DataType.FRAME); + metaOutput.setDimensions(compressInput1.getDim1(), -1); } - case REMOVE: { - checkNumParameters(2); - checkListParam(getFirstExpr()); - - // setup output properties - DataIdentifier out1 = (DataIdentifier) getOutputs()[0]; - DataIdentifier out2 = (DataIdentifier) getOutputs()[1]; + else + raiseValidateError("Compress/DeCompress instruction not allowed in dml script"); + break; - // Output1 - list after removal - long nrow = getFirstExpr().getOutput().getDim1() > 0 ? getFirstExpr().getOutput().getDim1() + 1 : -1; - out1.setDataType(DataType.LIST); - out1.setValueType(getFirstExpr().getOutput().getValueType()); - out1.setDimensions(nrow, 1); - out1.setBlocksize(getFirstExpr().getOutput().getBlocksize()); - - // Output2 - list of removed element - out2.setDataType(DataType.LIST); - out2.setValueType(getFirstExpr().getOutput().getValueType()); - out2.setDimensions(1, 1); - out2.setBlocksize(getFirstExpr().getOutput().getBlocksize()); - - break; - } - case SVD: - checkNumParameters(1); - checkMatrixParam(getFirstExpr()); - - long minMN = Math.min(getFirstExpr().getOutput().getDim1(), getFirstExpr().getOutput().getDim2()); - - // setup output properties - DataIdentifier svdOut1 = (DataIdentifier) getOutputs()[0]; - DataIdentifier svdOut2 = (DataIdentifier) getOutputs()[1]; - DataIdentifier svdOut3 = (DataIdentifier) getOutputs()[2]; - - // Output 1 - svdOut1.setDataType(DataType.MATRIX); - svdOut1.setValueType(ValueType.FP64); - svdOut1.setDimensions(getFirstExpr().getOutput().getDim1(), minMN); - svdOut1.setBlocksize(getFirstExpr().getOutput().getBlocksize()); - - // Output 2 - svdOut2.setDataType(DataType.MATRIX); - svdOut2.setValueType(ValueType.FP64); - svdOut2.setDimensions(minMN, minMN); - svdOut2.setBlocksize(getFirstExpr().getOutput().getBlocksize()); - - // Output 3 - svdOut3.setDataType(DataType.MATRIX); - svdOut3.setValueType(ValueType.FP64); - svdOut3.setDimensions(getFirstExpr().getOutput().getDim2(), minMN); - svdOut3.setBlocksize(getFirstExpr().getOutput().getBlocksize()); - - break; - - case COMPRESS: - if (OptimizerUtils.ALLOW_SCRIPT_LEVEL_COMPRESS_COMMAND) { - Expression expressionTwo = getSecondExpr(); - checkNumParameters(getSecondExpr() != null ? 2 : 1); - checkMatrixFrameParam(getFirstExpr()); - if (expressionTwo != null) - checkMatrixParam(getSecondExpr()); - - Identifier compressInput1 = getFirstExpr().getOutput(); - // Identifier compressInput2 = getSecondExpr().getOutput(); - - DataIdentifier compressOutput = (DataIdentifier) getOutputs()[0]; - compressOutput.setDataType(DataType.MATRIX); - compressOutput.setDimensions(compressInput1.getDim1(), compressInput1.getDim2()); - compressOutput.setBlocksize(compressInput1.getBlocksize()); - compressOutput.setValueType(compressInput1.getValueType()); - - DataIdentifier metaOutput = (DataIdentifier) getOutputs()[1]; - metaOutput.setDataType(DataType.FRAME); - metaOutput.setDimensions(compressInput1.getDim1(), -1); - } else - raiseValidateError("Compress/DeCompress instruction not allowed in dml script"); - break; - - default: // always unconditional - raiseValidateError("Unknown Builtin Function opcode: " + _opcode, false); + default: //always unconditional + raiseValidateError("Unknown Builtin Function opcode: " + _opcode, false); } } - + private static void setDimensions(DataIdentifier out, Expression exp) { out.setDataType(DataType.MATRIX); out.setValueType(ValueType.FP64); out.setDimensions(exp.getOutput().getDim1(), exp.getOutput().getDim2()); out.setBlocksize(exp.getOutput().getBlocksize()); } - - private static ArrayList orderDnnParams(ArrayList paramExpression, - int skip) { + + private static ArrayList orderDnnParams(ArrayList paramExpression, int skip) { ArrayList newParams = new ArrayList<>(); - for (int i = 0; i < skip; i++) + for(int i = 0; i < skip; i++) newParams.add(paramExpression.get(i)); - String[] orderedParams = { - "stride1", "stride2", "padding1", "padding2", - "input_shape1", "input_shape2", "input_shape3", "input_shape4", - "filter_shape1", "filter_shape2", "filter_shape3", "filter_shape4" + String [] orderedParams = { + "stride1", "stride2", "padding1", "padding2", + "input_shape1", "input_shape2", "input_shape3", "input_shape4", + "filter_shape1", "filter_shape2", "filter_shape3", "filter_shape4" }; - for (int i = 0; i < orderedParams.length; i++) { + for(int i = 0; i < orderedParams.length; i++) { boolean found = false; - for (ParameterExpression param : paramExpression) { - if (param.getName() != null && param.getName().equals(orderedParams[i])) { + for(ParameterExpression param : paramExpression) { + if(param.getName() != null && param.getName().equals(orderedParams[i])) { found = true; newParams.add(param); } } - if (!found) { + if(!found) { throw new LanguageException("Incorrect parameters. Expected " + orderedParams[i] + " to be expanded."); } } @@ -546,77 +542,74 @@ private static ArrayList replaceListParams(ArrayList newParamExpression = new ArrayList<>(); int i = startIndex; - int j = 1; // Assumption: sequential ordering pool_size1, pool_size2 + int j = 1; // Assumption: sequential ordering pool_size1, pool_size2 for (ParameterExpression expr : paramExpression) { - if (expr.getName() != null && expr.getName().equals(inputVarName + j)) { + if(expr.getName() != null && expr.getName().equals(inputVarName + j)) { newParamExpression.add(new ParameterExpression(outputVarName + i, expr.getExpr())); - i++; - j++; - } else { + i++; j++; + } + else { newParamExpression.add(expr); } } return newParamExpression; } - private static ArrayList expandListParams(ArrayList paramExpression, + private static ArrayList expandListParams(ArrayList paramExpression, HashSet paramsToExpand) { ArrayList newParamExpressions = new ArrayList<>(); - for (ParameterExpression expr : paramExpression) { - if (paramsToExpand.contains(expr.getName())) { - if (expr.getExpr() instanceof ExpressionList) { + for(ParameterExpression expr : paramExpression) { + if(paramsToExpand.contains(expr.getName())) { + if(expr.getExpr() instanceof ExpressionList) { int i = 1; - for (Expression e : ((ExpressionList) expr.getExpr()).getValue()) { + for(Expression e : ((ExpressionList)expr.getExpr()).getValue()) { newParamExpressions.add(new ParameterExpression(expr.getName() + i, e)); i++; } } - } else if (expr.getExpr() instanceof ExpressionList) { - throw new LanguageException("The parameter " + expr.getName() - + " cannot be list or is not supported for the given function"); - } else { + } + else if(expr.getExpr() instanceof ExpressionList) { + throw new LanguageException("The parameter " + expr.getName() + " cannot be list or is not supported for the given function"); + } + else { newParamExpressions.add(expr); } } return newParamExpressions; } - + private ArrayList expandDnnArguments(ArrayList paramExpression) { try { - if (_opcode == Builtins.CONV2D || _opcode == Builtins.CONV2D_BACKWARD_FILTER + if(_opcode == Builtins.CONV2D || _opcode == Builtins.CONV2D_BACKWARD_FILTER || _opcode == Builtins.CONV2D_BACKWARD_DATA) { HashSet expand = new HashSet<>(); - expand.add("input_shape"); - expand.add("filter_shape"); - expand.add("stride"); - expand.add("padding"); + expand.add("input_shape"); expand.add("filter_shape"); expand.add("stride"); expand.add("padding"); paramExpression = expandListParams(paramExpression, expand); paramExpression = orderDnnParams(paramExpression, 2); - } else if (_opcode == Builtins.MAX_POOL || _opcode == Builtins.AVG_POOL || + } + else if(_opcode == Builtins.MAX_POOL || _opcode == Builtins.AVG_POOL || _opcode == Builtins.MAX_POOL_BACKWARD || _opcode == Builtins.AVG_POOL_BACKWARD) { HashSet expand = new HashSet<>(); - expand.add("input_shape"); - expand.add("pool_size"); - expand.add("stride"); - expand.add("padding"); + expand.add("input_shape"); expand.add("pool_size"); expand.add("stride"); expand.add("padding"); paramExpression = expandListParams(paramExpression, expand); paramExpression.add(new ParameterExpression("filter_shape1", new IntIdentifier(1, this))); paramExpression.add(new ParameterExpression("filter_shape2", new IntIdentifier(1, this))); paramExpression = replaceListParams(paramExpression, "pool_size", "filter_shape", 3); - if (_opcode == Builtins.MAX_POOL_BACKWARD || _opcode == Builtins.AVG_POOL_BACKWARD) + if(_opcode == Builtins.MAX_POOL_BACKWARD || _opcode == Builtins.AVG_POOL_BACKWARD) paramExpression = orderDnnParams(paramExpression, 2); else paramExpression = orderDnnParams(paramExpression, 1); } - } catch (LanguageException e) { + } + catch(LanguageException e) { throw new RuntimeException(e); } return paramExpression; } - + private boolean isValidNoArgumentFunction() { return getOpCode() == Builtins.TIME - || getOpCode() == Builtins.LIST; + || getOpCode() == Builtins.LIST; } /** @@ -624,1311 +617,1303 @@ private boolean isValidNoArgumentFunction() { * statement */ @Override - public void validateExpression(HashMap ids, HashMap constVars, - boolean conditional) { - for (int i = 0; i < _args.length; i++) { - if (_args[i] instanceof FunctionCallIdentifier) { + public void validateExpression(HashMap ids, HashMap constVars, boolean conditional) + { + for(int i=0; i < _args.length; i++ ) { + if (_args[i] instanceof FunctionCallIdentifier){ raiseValidateError("UDF function call not supported as parameter to built-in function call", false); } _args[i].validateExpression(ids, constVars, conditional); } - + // checkIdentifierParams(); String outputName = getTempName(); DataIdentifier output = new DataIdentifier(outputName); output.setParseInfo(this); - - if (getFirstExpr() == null && !isValidNoArgumentFunction()) { // time has no arguments + + if (getFirstExpr() == null && !isValidNoArgumentFunction()) { // time has no arguments raiseValidateError("Function " + this + " has no arguments.", false); } - Identifier id = (_args.length != 0) ? getFirstExpr().getOutput() : null; + Identifier id = (_args.length != 0) ? + getFirstExpr().getOutput() : null; if (_args.length != 0) output.setProperties(this.getFirstExpr().getOutput()); - output.setNnz(-1); // conservatively, cannot use input nnz! + output.setNnz(-1); //conservatively, cannot use input nnz! setOutput(output); - + switch (getOpCode()) { - case EVAL: - case EVALLIST: - if (_args.length == 0) - raiseValidateError("Function eval should provide at least one argument, i.e., the function name.", - false); - checkValueTypeParam(_args[0], ValueType.STRING); - boolean listReturn = (getOpCode() == Builtins.EVALLIST); - output.setDataType(listReturn ? DataType.LIST : DataType.MATRIX); - output.setValueType(listReturn ? ValueType.UNKNOWN : ValueType.FP64); - output.setDimensions(-1, -1); - output.setBlocksize(ConfigurationManager.getBlocksize()); - break; - case COLSUM: - case COLMAX: - case COLMIN: - case COLMEAN: - case COLPROD: - case COLSD: - case COLVAR: - // colSums(X); - checkNumParameters(1); - checkMatrixParam(getFirstExpr()); - output.setDataType(DataType.MATRIX); - output.setDimensions(1, id.getDim2()); - output.setBlocksize(id.getBlocksize()); - output.setValueType(id.getValueType()); - break; - case ROWSUM: - case ROWMAX: - case ROWINDEXMAX: - case ROWMIN: - case ROWINDEXMIN: - case ROWMEAN: - case ROWPROD: - case ROWSD: - case ROWVAR: - // rowSums(X); - checkNumParameters(1); - checkMatrixParam(getFirstExpr()); - output.setDataType(DataType.MATRIX); - output.setDimensions(id.getDim1(), 1); - output.setBlocksize(id.getBlocksize()); - output.setValueType(id.getValueType()); - break; - case SUM: - case PROD: - case TRACE: - case SD: - case VAR: - // sum(X); - checkNumParameters(1); - checkMatrixTensorParam(getFirstExpr()); - output.setDataType(DataType.SCALAR); - output.setDimensions(0, 0); - output.setBlocksize(0); - switch (id.getValueType()) { - case INT64: - case INT32: - case UINT8: - case UINT4: - case BOOLEAN: - output.setValueType(ValueType.INT64); - break; - case STRING: - case CHARACTER: - case FP64: - case FP32: - case HASH64: //default - output.setValueType(ValueType.FP64); - break; - case UNKNOWN: - throw new NotImplementedException(); - } - break; - - case MEAN: - // checkNumParameters(2, false); // mean(Y) or mean(Y,W) - if (getSecondExpr() != null) { - checkNumParameters(2); - } else { - checkNumParameters(1); - } - - checkMatrixParam(getFirstExpr()); - if (getSecondExpr() != null) { - // x = mean(Y,W); - checkMatchingDimensions(getFirstExpr(), getSecondExpr()); - } - - output.setDataType(DataType.SCALAR); - output.setDimensions(0, 0); - output.setBlocksize(0); - output.setValueType(id.getValueType()); - break; - - case XOR: - case BITWAND: - case BITWOR: - case BITWXOR: - case BITWSHIFTL: - case BITWSHIFTR: - checkNumParameters(2); - setBinaryOutputProperties(output); - break; - - case MIN: - case MAX: - // min(X), min(X,s), min(s,X), min(s,r), min(X,Y) - if (getSecondExpr() == null) { // unary - checkNumParameters(1); - checkMatrixParam(getFirstExpr()); - output.setDataType(DataType.SCALAR); - output.setValueType(id.getValueType()); - output.setDimensions(0, 0); - output.setBlocksize(0); - } else if (getAllExpr().length == 2) { // binary - checkNumParameters(2); - setBinaryOutputProperties(output); - } else { // nary - for (Expression e : getAllExpr()) - checkMatrixScalarParam(e); - setNaryOutputProperties(output); - } - break; - - case CUMSUM: - case CUMPROD: - case CUMSUMPROD: - case CUMMIN: - case CUMMAX: - // cumsum(X); - checkNumParameters(1); - checkMatrixParam(getFirstExpr()); - boolean cumSP = getOpCode() == Builtins.CUMSUMPROD; - if (cumSP && id.getDim2() > 2) - raiseValidateError("Cumsumprod only supported over two-column matrices", conditional); - - output.setDataType(DataType.MATRIX); - output.setDimensions(id.getDim1(), cumSP ? 1 : id.getDim2()); - output.setBlocksize(id.getBlocksize()); - output.setValueType(id.getValueType()); - - break; - case CAST_AS_SCALAR: - checkNumParameters(1); - checkDataTypeParam(getFirstExpr(), - DataType.MATRIX, DataType.FRAME, DataType.LIST); - if ((getFirstExpr().getOutput().getDim1() != -1 && getFirstExpr().getOutput().getDim1() != 1) - || (getFirstExpr().getOutput().getDim2() != -1 && getFirstExpr().getOutput().getDim2() != 1)) { - raiseValidateError( - "dimension mismatch while casting matrix to scalar: dim1: " - + getFirstExpr().getOutput().getDim1() - + " dim2 " + getFirstExpr().getOutput().getDim2(), - conditional, LanguageErrorCodes.INVALID_PARAMETERS); - } - output.setDataType(DataType.SCALAR); - output.setDimensions(0, 0); - output.setBlocksize(0); - output.setValueType((id.getValueType() != ValueType.UNKNOWN - || id.getDataType() == DataType.LIST) ? id.getValueType() : ValueType.FP64); - break; - case CAST_AS_MATRIX: - checkNumParameters(1); - checkDataTypeParam(getFirstExpr(), - DataType.SCALAR, DataType.FRAME, DataType.LIST); - output.setDataType(DataType.MATRIX); - output.setDimensions(id.getDim1(), id.getDim2()); - if (getFirstExpr().getOutput().getDataType() == DataType.SCALAR) - output.setDimensions(1, 1); // correction scalars - if (getFirstExpr().getOutput().getDataType() == DataType.LIST) - output.setDimensions(-1, -1); // correction list: arbitrary object - output.setBlocksize(id.getBlocksize()); - output.setValueType(ValueType.FP64); // matrices always in double - break; - case CAST_AS_LIST: // list unnesting - checkNumParameters(1); - checkDataTypeParam(getFirstExpr(), DataType.LIST); - output.setDataType(DataType.LIST); - output.setDimensions(-1, 1); - output.setBlocksize(id.getBlocksize()); - output.setValueType(ValueType.UNKNOWN); - break; - case TYPEOF: - case DETECTSCHEMA: - case COLNAMES: - checkNumParameters(1); - checkMatrixFrameParam(getFirstExpr()); - output.setDataType(DataType.FRAME); - output.setDimensions(1, id.getDim2()); - output.setBlocksize(id.getBlocksize()); - output.setValueType(ValueType.STRING); - break; - case CAST_AS_FRAME: - // operation as.frame - // overloaded to take either one argument or 2 where second is column names - if (getSecondExpr() == null) {// there is no column names - checkNumParameters(1); - } else { // there is column names - checkNumParameters(2); - checkDataTypeParam(getSecondExpr(), DataType.LIST); - } - - checkDataTypeParam(getFirstExpr(), DataType.SCALAR, DataType.MATRIX, DataType.LIST); - output.setDataType(DataType.FRAME); - output.setDimensions(id.getDim1(), id.getDim2()); - if (getFirstExpr().getOutput().getDataType() == DataType.SCALAR) - output.setDimensions(1, 1); // correction scalars - if (getFirstExpr().getOutput().getDataType() == DataType.LIST) - output.setDimensions(-1, -1); // correction list: arbitrary object - output.setBlocksize(id.getBlocksize()); - output.setValueType(id.getValueType()); - break; - case CAST_AS_DOUBLE: - checkNumParameters(1); - checkScalarParam(getFirstExpr()); - output.setDataType(DataType.SCALAR); - // output.setDataType(id.getDataType()); //TODO whenever we support multiple - // matrix value types, currently noop. - output.setDimensions(0, 0); - output.setBlocksize(0); - output.setValueType(ValueType.FP64); - break; - case CAST_AS_INT: - checkNumParameters(1); - checkScalarParam(getFirstExpr()); - output.setDataType(DataType.SCALAR); - // output.setDataType(id.getDataType()); //TODO whenever we support multiple - // matrix value types, currently noop. - output.setDimensions(0, 0); - output.setBlocksize(0); - output.setValueType(ValueType.INT64); - break; - case CAST_AS_BOOLEAN: - checkNumParameters(1); - checkScalarParam(getFirstExpr()); - output.setDataType(DataType.SCALAR); - // output.setDataType(id.getDataType()); //TODO whenever we support multiple - // matrix value types, currently noop. - output.setDimensions(0, 0); - output.setBlocksize(0); - output.setValueType(ValueType.BOOLEAN); - break; - - case IFELSE: - checkNumParameters(3); - setTernaryOutputProperties(output, conditional); - break; - - case CBIND: - case RBIND: - // scalar string append (string concatenation with \n) - if (getFirstExpr().getOutput().getDataType() == DataType.SCALAR) { - checkNumParameters(2); - checkScalarParam(getFirstExpr()); - checkScalarParam(getSecondExpr()); - checkValueTypeParam(getFirstExpr(), ValueType.STRING); - checkValueTypeParam(getSecondExpr(), ValueType.STRING); - } - // append (rbind/cbind) all the elements of a list - else if (getAllExpr().length == 1) { - checkDataTypeParam(getFirstExpr(), DataType.LIST); - } else { - if (getAllExpr().length < 2) - raiseValidateError("Invalid number of arguments for " + getOpCode(), conditional); - // list append - if (getFirstExpr().getOutput().getDataType().isList()) - for (int i = 1; i < getAllExpr().length; i++) - checkDataTypeParam(getExpr(i), DataType.SCALAR, DataType.MATRIX, DataType.FRAME, - DataType.LIST); - // matrix append (rbind/cbind) - else - for (int i = 0; i < getAllExpr().length; i++) - checkMatrixFrameParam(getExpr(i)); - } - - output.setDataType(id.getDataType()); - output.setValueType(id.getValueType()); - - // special handling of concatenating all list elements - if (id.getDataType() == DataType.LIST && getAllExpr().length == 1) { - output.setDataType(DataType.MATRIX); + case EVAL: + case EVALLIST: + if (_args.length == 0) + raiseValidateError("Function eval should provide at least one argument, i.e., the function name.", false); + checkValueTypeParam(_args[0], ValueType.STRING); + boolean listReturn = (getOpCode()==Builtins.EVALLIST); + output.setDataType(listReturn ? DataType.LIST : DataType.MATRIX); + output.setValueType(listReturn ? ValueType.UNKNOWN : ValueType.FP64); + output.setDimensions(-1, -1); + output.setBlocksize(ConfigurationManager.getBlocksize()); + break; + case COLSUM: + case COLMAX: + case COLMIN: + case COLMEAN: + case COLPROD: + case COLSD: + case COLVAR: + // colSums(X); + checkNumParameters(1); + checkMatrixParam(getFirstExpr()); + output.setDataType(DataType.MATRIX); + output.setDimensions(1, id.getDim2()); + output.setBlocksize (id.getBlocksize()); + output.setValueType(id.getValueType()); + break; + case ROWSUM: + case ROWMAX: + case ROWINDEXMAX: + case ROWMIN: + case ROWINDEXMIN: + case ROWMEAN: + case ROWPROD: + case ROWSD: + case ROWVAR: + //rowSums(X); + checkNumParameters(1); + checkMatrixParam(getFirstExpr()); + output.setDataType(DataType.MATRIX); + output.setDimensions(id.getDim1(), 1); + output.setBlocksize (id.getBlocksize()); + output.setValueType(id.getValueType()); + break; + case SUM: + case PROD: + case TRACE: + case SD: + case VAR: + // sum(X); + checkNumParameters(1); + checkMatrixTensorParam(getFirstExpr()); + output.setDataType(DataType.SCALAR); + output.setDimensions(0, 0); + output.setBlocksize(0); + switch (id.getValueType()) { + case INT64: + case INT32: + case UINT8: + case UINT4: + case BOOLEAN: + output.setValueType(ValueType.INT64); + break; + case STRING: + case CHARACTER: + case FP64: + case FP32: + case HASH64: //default output.setValueType(ValueType.FP64); - } - - // set output dimensions and validate consistency - long m1rlen = getFirstExpr().getOutput().getDim1(); - long m1clen = getFirstExpr().getOutput().getDim2(); - long appendDim1 = m1rlen, appendDim2 = m1clen; - - // best-effort dimension propagation and validation - if (id.getDataType() == DataType.LIST) { - appendDim1 = -1; - appendDim2 = -1; - } else { - for (int i = 1; i < getAllExpr().length; i++) { - long m2rlen = getExpr(i).getOutput().getDim1(); - long m2clen = getExpr(i).getOutput().getDim2(); - - if (getOpCode() == Builtins.CBIND) { - if (m1rlen >= 0 && m2rlen >= 0 && m1rlen != m2rlen) { - raiseValidateError("inputs to cbind must have same number of rows: input 1 rows: " + - m1rlen + ", input 2 rows: " + m2rlen, conditional, - LanguageErrorCodes.INVALID_PARAMETERS); - } - appendDim1 = (m2rlen >= 0) ? m2rlen : appendDim1; - appendDim2 = (appendDim2 >= 0 && m2clen >= 0) ? appendDim2 + m2clen : -1; - } else if (getOpCode() == Builtins.RBIND) { - if (m1clen >= 0 && m2clen >= 0 && m1clen != m2clen) { - raiseValidateError( - "inputs to rbind must have same number of columns: input 1 columns: " + - m1clen + ", input 2 columns: " + m2clen, - conditional, LanguageErrorCodes.INVALID_PARAMETERS); - } - appendDim1 = (appendDim1 >= 0 && m2rlen >= 0) ? appendDim1 + m2rlen : -1; - appendDim2 = (m2clen >= 0) ? m2clen : appendDim2; - } - } - } - - output.setDimensions(appendDim1, appendDim2); - output.setBlocksize(id.getBlocksize()); - - break; - - case PPRED: - // TODO: remove this when ppred has been removed from DML - raiseValidateError("ppred() has been deprecated. Please use the operator directly.", true); - - // ppred (X,Y, "<"); ppred (X,y, "<"); ppred (y,X, "<"); - checkNumParameters(3); - - DataType dt1 = getFirstExpr().getOutput().getDataType(); - DataType dt2 = getSecondExpr().getOutput().getDataType(); - - // check input data types - if (dt1 == DataType.SCALAR && dt2 == DataType.SCALAR) { - raiseValidateError("ppred() requires at least one matrix input.", conditional, - LanguageErrorCodes.INVALID_PARAMETERS); - } - if (dt1 == DataType.MATRIX) - checkMatrixParam(getFirstExpr()); - if (dt2 == DataType.MATRIX) - checkMatrixParam(getSecondExpr()); - - // check operator - if (getThirdExpr().getOutput().getDataType() != DataType.SCALAR || - getThirdExpr().getOutput().getValueType() != ValueType.STRING) { - raiseValidateError("Third argument in ppred() is not an operator ", conditional, - LanguageErrorCodes.INVALID_PARAMETERS); - } - - setBinaryOutputProperties(output); - break; - - case TRANS: - checkNumParameters(1); - checkMatrixParam(getFirstExpr()); - output.setDataType(DataType.MATRIX); - output.setDimensions(id.getDim2(), id.getDim1()); - output.setBlocksize(id.getBlocksize()); - output.setValueType(id.getValueType()); - break; - - case REV: - checkNumParameters(1); - checkMatrixParam(getFirstExpr()); - output.setDataType(DataType.MATRIX); - output.setDimensions(id.getDim1(), id.getDim2()); - output.setBlocksize(id.getBlocksize()); - output.setValueType(id.getValueType()); - break; - - case DIAG: - checkNumParameters(1); - checkMatrixParam(getFirstExpr()); - output.setDataType(DataType.MATRIX); - if (id.getDim2() != -1) { // type known - if (id.getDim2() == 1) { - // diag V2M - output.setDimensions(id.getDim1(), id.getDim1()); - } else { - if (id.getDim1() != id.getDim2()) { - raiseValidateError( - "diag can either: (1) create diagonal matrix from (n x 1) matrix, or (2) take diagonal from a square matrix. " - + "Error invoking diag on matrix with dimensions (" - + id.getDim1() + "," + id.getDim2() - + ") in " + this.toString(), - conditional, LanguageErrorCodes.INVALID_PARAMETERS); - } - // diag M2V - output.setDimensions(id.getDim1(), 1); - } - } - output.setBlocksize(id.getBlocksize()); - output.setValueType(id.getValueType()); - break; - case NROW: - case NCOL: - case LENGTH: - checkNumParameters(1); - checkDataTypeParam(getFirstExpr(), - DataType.FRAME, DataType.LIST, DataType.MATRIX); - output.setDataType(DataType.SCALAR); - output.setDimensions(0, 0); - output.setBlocksize(0); - output.setValueType(ValueType.INT64); - break; - case LINEAGE: - checkNumParameters(1); - checkDataTypeParam(getFirstExpr(), - DataType.MATRIX, DataType.FRAME, DataType.LIST); - output.setDataType(DataType.SCALAR); - output.setDimensions(0, 0); - output.setBlocksize(0); - output.setValueType(ValueType.STRING); - break; - case LIST: - output.setDataType(DataType.LIST); - output.setValueType(ValueType.UNKNOWN); - output.setDimensions(getAllExpr().length, 1); - output.setBlocksize(-1); - break; - case EXISTS: - checkNumParameters(1); - checkStringOrDataIdentifier(getFirstExpr()); - output.setDataType(DataType.SCALAR); - output.setDimensions(0, 0); - output.setBlocksize(0); - output.setValueType(ValueType.BOOLEAN); - break; - - // Contingency tables - case TABLE: - - /* - * Allowed #of arguments: 2,3,4,5,6 - * table(A,B) - * table(A,B,W) - * table(A,B,1) - * table(A,B,dim1,dim2) - * table(A,B,W,dim1,dim2) - * table(A,B,1,dim1,dim2) - * table(A,B,1,dim1,dim2,TRUE) - */ - - // Check for validity of input arguments, and setup output dimensions - - // First input: is always of type MATRIX - checkMatrixParam(getFirstExpr()); - - if (getSecondExpr() == null) - raiseValidateError("Invalid number of arguments to table(). " - + "The table() function requires 2, 3, 4, 5, or 6 arguments.", conditional); - - // Second input: can be MATRIX or SCALAR - // cases: table(A,B) or table(A,1) - if (getSecondExpr().getOutput().getDataType() == DataType.MATRIX) - checkMatchingDimensions(getFirstExpr(), getSecondExpr()); - - long outputDim1 = -1, outputDim2 = -1; - - switch (_args.length) { - case 2: - // nothing to do - break; - - case 3: - // case - table w/ weights - // - weights specified as a matrix: table(A,B,W) or table(A,1,W) - // - weights specified as a scalar: table(A,B,1) or table(A,1,1) - if (getThirdExpr().getOutput().getDataType() == DataType.MATRIX) - checkMatchingDimensions(getFirstExpr(), getThirdExpr()); - break; - - case 4: - // case - table w/ output dimensions: table(A,B,dim1,dim2) or - // table(A,1,dim1,dim2) - // third and fourth arguments must be scalars - if (getThirdExpr().getOutput().getDataType() != DataType.SCALAR - || _args[3].getOutput().getDataType() != DataType.SCALAR) { - raiseValidateError( - "Invalid argument types to table(): output dimensions must be of type scalar: " - + this.toString(), - conditional, LanguageErrorCodes.INVALID_PARAMETERS); - } else { - // constant propagation - if (getThirdExpr() instanceof DataIdentifier - && constVars.containsKey(((DataIdentifier) getThirdExpr()).getName()) - && !conditional) - _args[2] = constVars.get(((DataIdentifier) getThirdExpr()).getName()); - if (_args[3] instanceof DataIdentifier - && constVars.containsKey(((DataIdentifier) _args[3]).getName()) && !conditional) - _args[3] = constVars.get(((DataIdentifier) _args[3]).getName()); - - if (getThirdExpr().getOutput() instanceof ConstIdentifier) - outputDim1 = ((ConstIdentifier) getThirdExpr().getOutput()).getLongValue(); - if (_args[3].getOutput() instanceof ConstIdentifier) - outputDim2 = ((ConstIdentifier) _args[3].getOutput()).getLongValue(); - } - break; - - case 5: - case 6: - // case - table w/ weights and output dimensions: - // - table(A,B,W,dim1,dim2) or table(A,1,W,dim1,dim2) - // - table(A,B,1,dim1,dim2) or table(A,1,1,dim1,dim2) - - if (getThirdExpr().getOutput().getDataType() == DataType.MATRIX) - checkMatchingDimensions(getFirstExpr(), getThirdExpr()); - - // fourth and fifth arguments must be scalars - if (_args[3].getOutput().getDataType() != DataType.SCALAR - || _args[4].getOutput().getDataType() != DataType.SCALAR) { - raiseValidateError( - "Invalid argument types to table(): output dimensions must be of type scalar: " - + this.toString(), - conditional, LanguageErrorCodes.INVALID_PARAMETERS); - } else { - // constant propagation - if (_args[3] instanceof DataIdentifier - && constVars.containsKey(((DataIdentifier) _args[3]).getName()) && !conditional) - _args[3] = constVars.get(((DataIdentifier) _args[3]).getName()); - if (_args[4] instanceof DataIdentifier - && constVars.containsKey(((DataIdentifier) _args[4]).getName()) && !conditional) - _args[4] = constVars.get(((DataIdentifier) _args[4]).getName()); - - if (_args[3].getOutput() instanceof ConstIdentifier) - outputDim1 = ((ConstIdentifier) _args[3].getOutput()).getLongValue(); - if (_args[4].getOutput() instanceof ConstIdentifier) - outputDim2 = ((ConstIdentifier) _args[4].getOutput()).getLongValue(); - } - if (_args.length == 6) { - if (!_args[5].getOutput().isScalarBoolean()) - raiseValidateError( - "The 6th ctable parameter (outputEmptyBlocks) must be a boolean literal.", - conditional); - } - break; - - default: - raiseValidateError("Invalid number of arguments to table(): " - + this.toString(), conditional, LanguageErrorCodes.INVALID_PARAMETERS); - } - // The dimensions for the output matrix will be known only at the - // run time - output.setDimensions(outputDim1, outputDim2); - output.setBlocksize(-1); - output.setDataType(DataType.MATRIX); - output.setValueType(ValueType.FP64); - break; - - case MOMENT: - checkMatrixParam(getFirstExpr()); - if (getThirdExpr() != null) { - checkNumParameters(3); - checkMatrixParam(getSecondExpr()); - checkMatchingDimensions(getFirstExpr(), getSecondExpr()); - checkScalarParam(getThirdExpr()); - } else { - checkNumParameters(2); - checkScalarParam(getSecondExpr()); - } - - // output is a scalar - output.setDataType(DataType.SCALAR); - output.setValueType(ValueType.FP64); - output.setDimensions(0, 0); - output.setBlocksize(0); - break; - - case COV: - /* - * x = cov(V1,V2) or xw = cov(V1,V2,W) - */ - if (getThirdExpr() != null) { - checkNumParameters(3); - } else { - checkNumParameters(2); - } - checkMatrixParam(getFirstExpr()); - checkMatrixParam(getSecondExpr()); - checkMatchingDimensions(getFirstExpr(), getSecondExpr()); - - if (getThirdExpr() != null) { - checkMatrixParam(getThirdExpr()); - checkMatchingDimensions(getFirstExpr(), getThirdExpr()); - } - - // output is a scalar - output.setDataType(DataType.SCALAR); - output.setValueType(ValueType.FP64); - output.setDimensions(0, 0); - output.setBlocksize(0); - break; - - case QUANTILE: - /* - * q = quantile(V1,0.5) computes median in V1 - * or Q = quantile(V1,P) computes the vector of quantiles as specified by P - * or qw = quantile(V1,W,0.5) computes median when weights (W) are given - * or QW = quantile(V1,W,P) computes the vector of quantiles as specified by P, - * when weights (W) are given - */ - if (getThirdExpr() != null) { - checkNumParameters(3); - } else { - checkNumParameters(2); - } - - // first parameter must always be a 1D matrix - check1DMatrixParam(getFirstExpr()); - - // check for matching dimensions for other matrix parameters - if (getThirdExpr() != null) { - checkMatrixParam(getSecondExpr()); - checkMatchingDimensions(getFirstExpr(), getSecondExpr()); - } - - // set the properties for _output expression - // output dimensions = dimensions of second, if third is null - // = dimensions of the third, otherwise. - - if (getThirdExpr() != null) { - output.setDimensions(getThirdExpr().getOutput().getDim1(), getThirdExpr().getOutput().getDim2()); - output.setBlocksize(getThirdExpr().getOutput().getBlocksize()); - output.setDataType(getThirdExpr().getOutput().getDataType()); - } else { - output.setDimensions(getSecondExpr().getOutput().getDim1(), getSecondExpr().getOutput().getDim2()); - output.setBlocksize(getSecondExpr().getOutput().getBlocksize()); - output.setDataType(getSecondExpr().getOutput().getDataType()); - } - break; - - case INTERQUANTILE: - if (getThirdExpr() != null) { - checkNumParameters(3); - } else { - checkNumParameters(2); - } - checkMatrixParam(getFirstExpr()); - if (getThirdExpr() != null) { - // i.e., second input is weight vector - checkMatrixParam(getSecondExpr()); - checkMatchingDimensionsQuantile(); - } - - if ((getThirdExpr() == null && getSecondExpr().getOutput().getDataType() != DataType.SCALAR) - && (getThirdExpr() != null && getThirdExpr().getOutput().getDataType() != DataType.SCALAR)) { - - raiseValidateError("Invalid parameters to " + this.getOpCode(), conditional, - LanguageErrorCodes.INVALID_PARAMETERS); - } - - output.setValueType(id.getValueType()); - // output dimensions are unknown - output.setDimensions(-1, -1); - output.setBlocksize(-1); - output.setDataType(DataType.MATRIX); - break; - - case IQM: - /* - * Usage: iqm = InterQuartileMean(A,W); iqm = InterQuartileMean(A); - */ - if (getSecondExpr() != null) { - checkNumParameters(2); - } else { - checkNumParameters(1); - } - checkMatrixParam(getFirstExpr()); - - if (getSecondExpr() != null) { - // i.e., second input is weight vector - checkMatrixParam(getSecondExpr()); - checkMatchingDimensions(getFirstExpr(), getSecondExpr()); - } - - // Output is a scalar - output.setValueType(id.getValueType()); - output.setDimensions(0, 0); - output.setBlocksize(0); - output.setDataType(DataType.SCALAR); - - break; - - case ISNA: - case ISNAN: - case ISINF: + break; + case UNKNOWN: + throw new NotImplementedException(); + } + break; + + case MEAN: + //checkNumParameters(2, false); // mean(Y) or mean(Y,W) + if (getSecondExpr() != null) { + checkNumParameters(2); + } + else { + checkNumParameters(1); + } + + checkMatrixParam(getFirstExpr()); + if ( getSecondExpr() != null ) { + // x = mean(Y,W); + checkMatchingDimensions(getFirstExpr(), getSecondExpr()); + } + + output.setDataType(DataType.SCALAR); + output.setDimensions(0, 0); + output.setBlocksize(0); + output.setValueType(id.getValueType()); + break; + + case XOR: + case BITWAND: + case BITWOR: + case BITWXOR: + case BITWSHIFTL: + case BITWSHIFTR: + checkNumParameters(2); + setBinaryOutputProperties(output); + break; + + case MIN: + case MAX: + //min(X), min(X,s), min(s,X), min(s,r), min(X,Y) + if (getSecondExpr() == null) { //unary checkNumParameters(1); - checkMatrixScalarParam(getFirstExpr()); - output.setDataType(id.getDataType()); - output.setDimensions(id.getDim1(), id.getDim2()); - output.setBlocksize(id.getBlocksize()); - // TODO set output type to boolean when supported - output.setValueType(id.getValueType()); - break; - - case MEDIAN: - checkNumParameters((getSecondExpr() != null) ? 2 : 1); checkMatrixParam(getFirstExpr()); - - if (getSecondExpr() != null) { - // i.e., second input is weight vector - checkMatrixParam(getSecondExpr()); - checkMatchingDimensions(getFirstExpr(), getSecondExpr()); - } - - // Output is a scalar + output.setDataType(DataType.SCALAR); output.setValueType(id.getValueType()); output.setDimensions(0, 0); output.setBlocksize(0); - output.setDataType(DataType.SCALAR); - - break; - - case SAMPLE: { - Expression[] in = getAllExpr(); - - for (Expression e : in) - checkScalarParam(e); - - if (in[0].getOutput().getValueType() != ValueType.FP64 - && in[0].getOutput().getValueType() != ValueType.INT64) - throw new LanguageException("First argument to sample() must be a number."); - if (in[1].getOutput().getValueType() != ValueType.FP64 - && in[1].getOutput().getValueType() != ValueType.INT64) - throw new LanguageException("Second argument to sample() must be a number."); - - boolean check = false; - if (isConstant(in[0]) && isConstant(in[1])) { - long range = ((ConstIdentifier) in[0]).getLongValue(); - long size = ((ConstIdentifier) in[1]).getLongValue(); - if (range < size) - check = true; - } - - if (in.length == 4) { - checkNumParameters(4); - if (in[3].getOutput().getValueType() != ValueType.INT64) - throw new LanguageException("Fourth argument, seed, to sample() must be an integer value."); - if (in[2].getOutput().getValueType() != ValueType.BOOLEAN) - throw new LanguageException( - "Third argument to sample() must either denote replacement policy (boolean) or seed (integer)."); - } else if (in.length == 3) { - checkNumParameters(3); - if (in[2].getOutput().getValueType() != ValueType.BOOLEAN - && in[2].getOutput().getValueType() != ValueType.INT64) - throw new LanguageException( - "Third argument to sample() must either denote replacement policy (boolean) or seed (integer)."); - } - - if (check && in.length >= 3 - && isConstant(in[2]) - && in[2].getOutput().getValueType() == ValueType.BOOLEAN - && !((BooleanIdentifier) in[2]).getValue()) - throw new LanguageException("Sample (size=" + ((ConstIdentifier) in[0]).getLongValue() - + ") larger than population (size=" + ((ConstIdentifier) in[1]).getLongValue() - + ") can only be generated with replacement."); - - // Output is a column vector - output.setDataType(DataType.MATRIX); - output.setValueType(ValueType.FP64); - - if (isConstant(in[1])) - output.setDimensions(((ConstIdentifier) in[1]).getLongValue(), 1); - else - output.setDimensions(-1, 1); - setBlocksize(id.getBlocksize()); - - break; } - case SEQ: + else if( getAllExpr().length == 2 ) { //binary + checkNumParameters(2); + setBinaryOutputProperties(output); + } + else { //nary + for( Expression e : getAllExpr() ) + checkMatrixScalarParam(e); + setNaryOutputProperties(output); + } + break; + + case CUMSUM: + case CUMPROD: + case CUMSUMPROD: + case CUMMIN: + case CUMMAX: + // cumsum(X); + checkNumParameters(1); + checkMatrixParam(getFirstExpr()); + boolean cumSP = getOpCode() == Builtins.CUMSUMPROD; + if( cumSP && id.getDim2() > 2 ) + raiseValidateError("Cumsumprod only supported over two-column matrices", conditional); + + output.setDataType(DataType.MATRIX); + output.setDimensions(id.getDim1(), cumSP ? 1 : id.getDim2()); + output.setBlocksize (id.getBlocksize()); + output.setValueType(id.getValueType()); + + break; + case CAST_AS_SCALAR: + checkNumParameters(1); + checkDataTypeParam(getFirstExpr(), + DataType.MATRIX, DataType.FRAME, DataType.LIST); + if (( getFirstExpr().getOutput().getDim1() != -1 && getFirstExpr().getOutput().getDim1() !=1) + || ( getFirstExpr().getOutput().getDim2() != -1 && getFirstExpr().getOutput().getDim2() !=1)) { + raiseValidateError("dimension mismatch while casting matrix to scalar: dim1: " + getFirstExpr().getOutput().getDim1() + + " dim2 " + getFirstExpr().getOutput().getDim2(), conditional, LanguageErrorCodes.INVALID_PARAMETERS); + } + output.setDataType(DataType.SCALAR); + output.setDimensions(0, 0); + output.setBlocksize(0); + output.setValueType((id.getValueType()!=ValueType.UNKNOWN + || id.getDataType()==DataType.LIST) ? id.getValueType() : ValueType.FP64); + break; + case CAST_AS_MATRIX: + checkNumParameters(1); + checkDataTypeParam(getFirstExpr(), + DataType.SCALAR, DataType.FRAME, DataType.LIST); + output.setDataType(DataType.MATRIX); + output.setDimensions(id.getDim1(), id.getDim2()); + if( getFirstExpr().getOutput().getDataType()==DataType.SCALAR ) + output.setDimensions(1, 1); //correction scalars + if( getFirstExpr().getOutput().getDataType()==DataType.LIST ) + output.setDimensions(-1, -1); //correction list: arbitrary object + output.setBlocksize(id.getBlocksize()); + output.setValueType(ValueType.FP64); //matrices always in double + break; + case CAST_AS_LIST: //list unnesting + checkNumParameters(1); + checkDataTypeParam(getFirstExpr(), DataType.LIST); + output.setDataType(DataType.LIST); + output.setDimensions(-1, 1); + output.setBlocksize(id.getBlocksize()); + output.setValueType(ValueType.UNKNOWN); + break; + case TYPEOF: + case DETECTSCHEMA: + case COLNAMES: + checkNumParameters(1); + checkMatrixFrameParam(getFirstExpr()); + output.setDataType(DataType.FRAME); + output.setDimensions(1, id.getDim2()); + output.setBlocksize (id.getBlocksize()); + output.setValueType(ValueType.STRING); + break; + case CAST_AS_FRAME: + // operation as.frame + // overloaded to take either one argument or 2 where second is column names + if( getSecondExpr() == null) {// there is no column names + checkNumParameters(1); + } + else{ // there is column names + checkNumParameters(2); + checkDataTypeParam(getSecondExpr(), DataType.LIST); + } - // basic parameter validation + checkDataTypeParam(getFirstExpr(), DataType.SCALAR, DataType.MATRIX, DataType.LIST); + output.setDataType(DataType.FRAME); + output.setDimensions(id.getDim1(), id.getDim2()); + if(getFirstExpr().getOutput().getDataType() == DataType.SCALAR) + output.setDimensions(1, 1); // correction scalars + if(getFirstExpr().getOutput().getDataType() == DataType.LIST) + output.setDimensions(-1, -1); // correction list: arbitrary object + output.setBlocksize(id.getBlocksize()); + output.setValueType(id.getValueType()); + break; + case CAST_AS_DOUBLE: + checkNumParameters(1); + checkScalarParam(getFirstExpr()); + output.setDataType(DataType.SCALAR); + //output.setDataType(id.getDataType()); //TODO whenever we support multiple matrix value types, currently noop. + output.setDimensions(0, 0); + output.setBlocksize(0); + output.setValueType(ValueType.FP64); + break; + case CAST_AS_INT: + checkNumParameters(1); + checkScalarParam(getFirstExpr()); + output.setDataType(DataType.SCALAR); + //output.setDataType(id.getDataType()); //TODO whenever we support multiple matrix value types, currently noop. + output.setDimensions(0, 0); + output.setBlocksize(0); + output.setValueType(ValueType.INT64); + break; + case CAST_AS_BOOLEAN: + checkNumParameters(1); + checkScalarParam(getFirstExpr()); + output.setDataType(DataType.SCALAR); + //output.setDataType(id.getDataType()); //TODO whenever we support multiple matrix value types, currently noop. + output.setDimensions(0, 0); + output.setBlocksize(0); + output.setValueType(ValueType.BOOLEAN); + break; + + case IFELSE: + checkNumParameters(3); + setTernaryOutputProperties(output, conditional); + break; + + case CBIND: + case RBIND: + //scalar string append (string concatenation with \n) + if( getFirstExpr().getOutput().getDataType()==DataType.SCALAR ) { + checkNumParameters(2); checkScalarParam(getFirstExpr()); checkScalarParam(getSecondExpr()); - if (getThirdExpr() != null) { - checkNumParameters(3); - checkScalarParam(getThirdExpr()); - } else - checkNumParameters(2); - - // constant propagation (from, to, incr) - if (!conditional) { - if (getFirstExpr() instanceof DataIdentifier - && constVars.containsKey(((DataIdentifier) getFirstExpr()).getName())) - _args[0] = constVars.get(((DataIdentifier) getFirstExpr()).getName()); - if (getSecondExpr() instanceof DataIdentifier - && constVars.containsKey(((DataIdentifier) getSecondExpr()).getName())) - _args[1] = constVars.get(((DataIdentifier) getSecondExpr()).getName()); - if (getThirdExpr() != null && getThirdExpr() instanceof DataIdentifier - && constVars.containsKey(((DataIdentifier) getThirdExpr()).getName())) - _args[2] = constVars.get(((DataIdentifier) getThirdExpr()).getName()); - } - - // check if dimensions can be inferred - long dim1 = -1, dim2 = 1; - if (isConstant(getFirstExpr()) && isConstant(getSecondExpr()) - && (getThirdExpr() != null ? isConstant(getThirdExpr()) : true)) { - double from, to, incr; - try { - from = getDoubleValue(getFirstExpr()); - to = getDoubleValue(getSecondExpr()); - - // Setup the value of increment - // default value: 1 if from <= to; -1 if from > to - if (getThirdExpr() == null) { - expandArguments(); - _args[2] = new DoubleIdentifier(((from > to) ? -1.0 : 1.0), this); + checkValueTypeParam(getFirstExpr(), ValueType.STRING); + checkValueTypeParam(getSecondExpr(), ValueType.STRING); + } + // append (rbind/cbind) all the elements of a list + else if( getAllExpr().length == 1 ) { + checkDataTypeParam(getFirstExpr(), DataType.LIST); + } + else { + if( getAllExpr().length < 2 ) + raiseValidateError("Invalid number of arguments for "+getOpCode(), conditional); + //list append + if(getFirstExpr().getOutput().getDataType().isList() ) + for(int i=1; i= 0 && m2rlen >= 0 && m1rlen!=m2rlen) { + raiseValidateError("inputs to cbind must have same number of rows: input 1 rows: " + + m1rlen+", input 2 rows: "+m2rlen, conditional, LanguageErrorCodes.INVALID_PARAMETERS); } - incr = getDoubleValue(getThirdExpr()); - - } catch (LanguageException e) { - throw new LanguageException("Arguments for seq() must be numeric."); + appendDim1 = (m2rlen>=0) ? m2rlen : appendDim1; + appendDim2 = (appendDim2>=0 && m2clen>=0) ? appendDim2 + m2clen : -1; + } + else if( getOpCode() == Builtins.RBIND ) { + if (m1clen >= 0 && m2clen >= 0 && m1clen!=m2clen) { + raiseValidateError("inputs to rbind must have same number of columns: input 1 columns: " + + m1clen+", input 2 columns: "+m2clen, conditional, LanguageErrorCodes.INVALID_PARAMETERS); + } + appendDim1 = (appendDim1>=0 && m2rlen>=0)? appendDim1 + m2rlen : -1; + appendDim2 = (m2clen>=0) ? m2clen : appendDim2; } - - if ((from > to) && (incr >= 0)) - throw new LanguageException("Wrong sign for the increment in a call to seq()"); - - // Both end points of the range must included i.e., [from,to] both inclusive. - // Note that, "to" is included only if (to-from) is perfectly divisible by incr - // For example, seq(0,1,0.5) produces (0.0 0.5 1.0) whereas seq(0,1,0.6) - // produces only (0.0 0.6) but not (0.0 0.6 1.0) - dim1 = UtilFunctions.getSeqLength(from, to, incr); } - output.setDataType(DataType.MATRIX); - output.setValueType(ValueType.FP64); - output.setDimensions(dim1, dim2); - output.setBlocksize(0); - break; - - case SOLVE: - checkNumParameters(2); + } + + output.setDimensions(appendDim1, appendDim2); + output.setBlocksize (id.getBlocksize()); + + break; + + case PPRED: + // TODO: remove this when ppred has been removed from DML + raiseValidateError("ppred() has been deprecated. Please use the operator directly.", true); + + // ppred (X,Y, "<"); ppred (X,y, "<"); ppred (y,X, "<"); + checkNumParameters(3); + + DataType dt1 = getFirstExpr().getOutput().getDataType(); + DataType dt2 = getSecondExpr().getOutput().getDataType(); + + //check input data types + if( dt1 == DataType.SCALAR && dt2 == DataType.SCALAR ) { + raiseValidateError("ppred() requires at least one matrix input.", conditional, LanguageErrorCodes.INVALID_PARAMETERS); + } + if( dt1 == DataType.MATRIX ) checkMatrixParam(getFirstExpr()); + if( dt2 == DataType.MATRIX ) checkMatrixParam(getSecondExpr()); - - if (getSecondExpr().getOutput().dimsKnown() && !is1DMatrix(getSecondExpr())) - raiseValidateError("Second input to solve() must be a vector", conditional); - - if (getFirstExpr().getOutput().dimsKnown() && getSecondExpr().getOutput().dimsKnown() && - getFirstExpr().getOutput().getDim1() != getSecondExpr().getOutput().getDim1() && - getFirstExpr().getOutput().getDim1() != getFirstExpr().getOutput().getDim2()) - raiseValidateError("Dimension mismatch in a call to solve()", conditional); - - output.setDataType(DataType.MATRIX); - output.setValueType(ValueType.FP64); - output.setDimensions(getFirstExpr().getOutput().getDim2(), 1); - output.setBlocksize(0); + + //check operator + if (getThirdExpr().getOutput().getDataType() != DataType.SCALAR || + getThirdExpr().getOutput().getValueType() != ValueType.STRING) + { + raiseValidateError("Third argument in ppred() is not an operator ", conditional, LanguageErrorCodes.INVALID_PARAMETERS); + } + + setBinaryOutputProperties(output); + break; + + case TRANS: + checkNumParameters(1); + checkMatrixParam(getFirstExpr()); + output.setDataType(DataType.MATRIX); + output.setDimensions(id.getDim2(), id.getDim1()); + output.setBlocksize (id.getBlocksize()); + output.setValueType(id.getValueType()); + break; + + case REV: + checkNumParameters(1); + checkMatrixParam(getFirstExpr()); + output.setDataType(DataType.MATRIX); + output.setDimensions(id.getDim1(), id.getDim2()); + output.setBlocksize (id.getBlocksize()); + output.setValueType(id.getValueType()); + break; + + case DIAG: + checkNumParameters(1); + checkMatrixParam(getFirstExpr()); + output.setDataType(DataType.MATRIX); + if( id.getDim2() != -1 ) { //type known + if ( id.getDim2() == 1 ) + { + //diag V2M + output.setDimensions(id.getDim1(), id.getDim1()); + } + else + { + if (id.getDim1() != id.getDim2()) { + raiseValidateError("diag can either: (1) create diagonal matrix from (n x 1) matrix, or (2) take diagonal from a square matrix. " + + "Error invoking diag on matrix with dimensions (" + + id.getDim1() + "," + id.getDim2() + + ") in " + this.toString(), conditional, LanguageErrorCodes.INVALID_PARAMETERS); + } + //diag M2V + output.setDimensions(id.getDim1(), 1); + } + } + output.setBlocksize(id.getBlocksize()); + output.setValueType(id.getValueType()); + break; + case NROW: + case NCOL: + case LENGTH: + checkNumParameters(1); + checkDataTypeParam(getFirstExpr(), + DataType.FRAME, DataType.LIST, DataType.MATRIX); + output.setDataType(DataType.SCALAR); + output.setDimensions(0, 0); + output.setBlocksize(0); + output.setValueType(ValueType.INT64); + break; + case LINEAGE: + checkNumParameters(1); + checkDataTypeParam(getFirstExpr(), + DataType.MATRIX, DataType.FRAME, DataType.LIST); + output.setDataType(DataType.SCALAR); + output.setDimensions(0, 0); + output.setBlocksize(0); + output.setValueType(ValueType.STRING); + break; + case LIST: + output.setDataType(DataType.LIST); + output.setValueType(ValueType.UNKNOWN); + output.setDimensions(getAllExpr().length, 1); + output.setBlocksize(-1); + break; + case EXISTS: + checkNumParameters(1); + checkStringOrDataIdentifier(getFirstExpr()); + output.setDataType(DataType.SCALAR); + output.setDimensions(0, 0); + output.setBlocksize(0); + output.setValueType(ValueType.BOOLEAN); + break; + + // Contingency tables + case TABLE: + + /* + * Allowed #of arguments: 2,3,4,5,6 + * table(A,B) + * table(A,B,W) + * table(A,B,1) + * table(A,B,dim1,dim2) + * table(A,B,W,dim1,dim2) + * table(A,B,1,dim1,dim2) + * table(A,B,1,dim1,dim2,TRUE) + */ + + // Check for validity of input arguments, and setup output dimensions + + // First input: is always of type MATRIX + checkMatrixParam(getFirstExpr()); + + if (getSecondExpr() == null) + raiseValidateError("Invalid number of arguments to table(). " + + "The table() function requires 2, 3, 4, 5, or 6 arguments.", conditional); + + // Second input: can be MATRIX or SCALAR + // cases: table(A,B) or table(A,1) + if ( getSecondExpr().getOutput().getDataType() == DataType.MATRIX) + checkMatchingDimensions(getFirstExpr(),getSecondExpr()); + + long outputDim1=-1, outputDim2=-1; + + switch(_args.length) { + case 2: + // nothing to do + break; + + case 3: + // case - table w/ weights + // - weights specified as a matrix: table(A,B,W) or table(A,1,W) + // - weights specified as a scalar: table(A,B,1) or table(A,1,1) + if ( getThirdExpr().getOutput().getDataType() == DataType.MATRIX) + checkMatchingDimensions(getFirstExpr(),getThirdExpr()); + break; + + case 4: + // case - table w/ output dimensions: table(A,B,dim1,dim2) or table(A,1,dim1,dim2) + // third and fourth arguments must be scalars + if ( getThirdExpr().getOutput().getDataType() != DataType.SCALAR || _args[3].getOutput().getDataType() != DataType.SCALAR ) { + raiseValidateError("Invalid argument types to table(): output dimensions must be of type scalar: " + + this.toString(), conditional, LanguageErrorCodes.INVALID_PARAMETERS); + } + else { + // constant propagation + if( getThirdExpr() instanceof DataIdentifier && constVars.containsKey(((DataIdentifier)getThirdExpr()).getName()) && !conditional ) + _args[2] = constVars.get(((DataIdentifier)getThirdExpr()).getName()); + if( _args[3] instanceof DataIdentifier && constVars.containsKey(((DataIdentifier)_args[3]).getName()) && !conditional ) + _args[3] = constVars.get(((DataIdentifier)_args[3]).getName()); + + if ( getThirdExpr().getOutput() instanceof ConstIdentifier ) + outputDim1 = ((ConstIdentifier) getThirdExpr().getOutput()).getLongValue(); + if ( _args[3].getOutput() instanceof ConstIdentifier ) + outputDim2 = ((ConstIdentifier) _args[3].getOutput()).getLongValue(); + } + break; + + case 5: + case 6: + // case - table w/ weights and output dimensions: + // - table(A,B,W,dim1,dim2) or table(A,1,W,dim1,dim2) + // - table(A,B,1,dim1,dim2) or table(A,1,1,dim1,dim2) + + if ( getThirdExpr().getOutput().getDataType() == DataType.MATRIX) + checkMatchingDimensions(getFirstExpr(),getThirdExpr()); + + // fourth and fifth arguments must be scalars + if ( _args[3].getOutput().getDataType() != DataType.SCALAR || _args[4].getOutput().getDataType() != DataType.SCALAR ) { + raiseValidateError("Invalid argument types to table(): output dimensions must be of type scalar: " + + this.toString(), conditional, LanguageErrorCodes.INVALID_PARAMETERS); + } + else { + // constant propagation + if( _args[3] instanceof DataIdentifier && constVars.containsKey(((DataIdentifier)_args[3]).getName()) && !conditional ) + _args[3] = constVars.get(((DataIdentifier)_args[3]).getName()); + if( _args[4] instanceof DataIdentifier && constVars.containsKey(((DataIdentifier)_args[4]).getName()) && !conditional ) + _args[4] = constVars.get(((DataIdentifier)_args[4]).getName()); + + if ( _args[3].getOutput() instanceof ConstIdentifier ) + outputDim1 = ((ConstIdentifier) _args[3].getOutput()).getLongValue(); + if ( _args[4].getOutput() instanceof ConstIdentifier ) + outputDim2 = ((ConstIdentifier) _args[4].getOutput()).getLongValue(); + } + if( _args.length == 6 ) { + if( !_args[5].getOutput().isScalarBoolean() ) + raiseValidateError("The 6th ctable parameter (outputEmptyBlocks) must be a boolean literal.", conditional); + } break; - case INVERSE: - checkNumParameters(1); - checkMatrixParam(getFirstExpr()); - - output.setDataType(DataType.MATRIX); - output.setValueType(ValueType.FP64); + default: + raiseValidateError("Invalid number of arguments to table(): " + + this.toString(), conditional, LanguageErrorCodes.INVALID_PARAMETERS); + } + // The dimensions for the output matrix will be known only at the + // run time + output.setDimensions(outputDim1, outputDim2); + output.setBlocksize (-1); + output.setDataType(DataType.MATRIX); + output.setValueType(ValueType.FP64); + break; + + case MOMENT: + checkMatrixParam(getFirstExpr()); + if (getThirdExpr() != null) { + checkNumParameters(3); + checkMatrixParam(getSecondExpr()); + checkMatchingDimensions(getFirstExpr(),getSecondExpr()); + checkScalarParam(getThirdExpr()); + } + else { + checkNumParameters(2); + checkScalarParam(getSecondExpr()); + } - Identifier in = getFirstExpr().getOutput(); - if (in.dimsKnown() && in.getDim1() != in.getDim2()) - raiseValidateError("Input to inv() must be square matrix -- given: a " + in.getDim1() + "x" - + in.getDim2() + " matrix.", conditional); + // output is a scalar + output.setDataType(DataType.SCALAR); + output.setValueType(ValueType.FP64); + output.setDimensions(0, 0); + output.setBlocksize(0); + break; + + case COV: + /* + * x = cov(V1,V2) or xw = cov(V1,V2,W) + */ + if (getThirdExpr() != null) { + checkNumParameters(3); + } + else { + checkNumParameters(2); + } + checkMatrixParam(getFirstExpr()); + checkMatrixParam(getSecondExpr()); + checkMatchingDimensions(getFirstExpr(),getSecondExpr()); + + if (getThirdExpr() != null) { + checkMatrixParam(getThirdExpr()); + checkMatchingDimensions(getFirstExpr(), getThirdExpr()); + } - output.setDimensions(in.getDim1(), in.getDim2()); - output.setBlocksize(in.getBlocksize()); - break; + // output is a scalar + output.setDataType(DataType.SCALAR); + output.setValueType(ValueType.FP64); + output.setDimensions(0, 0); + output.setBlocksize(0); + break; + + case QUANTILE: + /* + * q = quantile(V1,0.5) computes median in V1 + * or Q = quantile(V1,P) computes the vector of quantiles as specified by P + * or qw = quantile(V1,W,0.5) computes median when weights (W) are given + * or QW = quantile(V1,W,P) computes the vector of quantiles as specified by P, when weights (W) are given + */ + if(getThirdExpr() != null) { + checkNumParameters(3); + } + else { + checkNumParameters(2); + } + + // first parameter must always be a 1D matrix + check1DMatrixParam(getFirstExpr()); + + // check for matching dimensions for other matrix parameters + if (getThirdExpr() != null) { + checkMatrixParam(getSecondExpr()); + checkMatchingDimensions(getFirstExpr(), getSecondExpr()); + } + + // set the properties for _output expression + // output dimensions = dimensions of second, if third is null + // = dimensions of the third, otherwise. + + if (getThirdExpr() != null) { + output.setDimensions(getThirdExpr().getOutput().getDim1(), getThirdExpr().getOutput().getDim2()); + output.setBlocksize(getThirdExpr().getOutput().getBlocksize()); + output.setDataType(getThirdExpr().getOutput().getDataType()); + } else { + output.setDimensions(getSecondExpr().getOutput().getDim1(), getSecondExpr().getOutput().getDim2()); + output.setBlocksize(getSecondExpr().getOutput().getBlocksize()); + output.setDataType(getSecondExpr().getOutput().getDataType()); + } + break; - case CHOLESKY: { - // A = L%*%t(L) where L is the lower triangular matrix - checkNumParameters(1); - checkMatrixParam(getFirstExpr()); + case INTERQUANTILE: + if (getThirdExpr() != null) { + checkNumParameters(3); + } + else { + checkNumParameters(2); + } + checkMatrixParam(getFirstExpr()); + if (getThirdExpr() != null) { + // i.e., second input is weight vector + checkMatrixParam(getSecondExpr()); + checkMatchingDimensionsQuantile(); + } - output.setDataType(DataType.MATRIX); - output.setValueType(ValueType.FP64); + if ((getThirdExpr() == null && getSecondExpr().getOutput().getDataType() != DataType.SCALAR) + && (getThirdExpr() != null && getThirdExpr().getOutput().getDataType() != DataType.SCALAR)) { + + raiseValidateError("Invalid parameters to "+ this.getOpCode(), conditional, LanguageErrorCodes.INVALID_PARAMETERS); + } - Identifier inA = getFirstExpr().getOutput(); - if (inA.dimsKnown() && inA.getDim1() != inA.getDim2()) - raiseValidateError("Input to cholesky() must be square matrix -- given: a " + inA.getDim1() + "x" - + inA.getDim2() + " matrix.", conditional); + output.setValueType(id.getValueType()); + // output dimensions are unknown + output.setDimensions(-1, -1); + output.setBlocksize(-1); + output.setDataType(DataType.MATRIX); + break; + + case IQM: + /* + * Usage: iqm = InterQuartileMean(A,W); iqm = InterQuartileMean(A); + */ + if (getSecondExpr() != null){ + checkNumParameters(2); + } + else { + checkNumParameters(1); + } + checkMatrixParam(getFirstExpr()); - output.setDimensions(inA.getDim1(), inA.getDim2()); - output.setBlocksize(inA.getBlocksize()); - break; + if (getSecondExpr() != null) { + // i.e., second input is weight vector + checkMatrixParam(getSecondExpr()); + checkMatchingDimensions(getFirstExpr(), getSecondExpr()); } - case OUTER: - Identifier id2 = this.getSecondExpr().getOutput(); + // Output is a scalar + output.setValueType(id.getValueType()); + output.setDimensions(0, 0); + output.setBlocksize(0); + output.setDataType(DataType.SCALAR); + + break; + + case ISNA: + case ISNAN: + case ISINF: + checkNumParameters(1); + checkMatrixScalarParam(getFirstExpr()); + output.setDataType(id.getDataType()); + output.setDimensions(id.getDim1(), id.getDim2()); + output.setBlocksize (id.getBlocksize()); + //TODO set output type to boolean when supported + output.setValueType(id.getValueType()); + break; + + case MEDIAN: + checkNumParameters((getSecondExpr()!=null) ? 2 : 1); + checkMatrixParam(getFirstExpr()); + + if (getSecondExpr() != null) { + // i.e., second input is weight vector + checkMatrixParam(getSecondExpr()); + checkMatchingDimensions(getFirstExpr(), getSecondExpr()); + } - // check input types and characteristics + // Output is a scalar + output.setValueType(id.getValueType()); + output.setDimensions(0, 0); + output.setBlocksize(0); + output.setDataType(DataType.SCALAR); + + break; + + case SAMPLE: + { + Expression[] in = getAllExpr(); + + for(Expression e : in) + checkScalarParam(e); + + if (in[0].getOutput().getValueType() != ValueType.FP64 && in[0].getOutput().getValueType() != ValueType.INT64) + throw new LanguageException("First argument to sample() must be a number."); + if (in[1].getOutput().getValueType() != ValueType.FP64 && in[1].getOutput().getValueType() != ValueType.INT64) + throw new LanguageException("Second argument to sample() must be a number."); + + boolean check = false; + if ( isConstant(in[0]) && isConstant(in[1]) ) + { + long range = ((ConstIdentifier)in[0]).getLongValue(); + long size = ((ConstIdentifier)in[1]).getLongValue(); + if ( range < size ) + check = true; + } + + if(in.length == 4 ) + { + checkNumParameters(4); + if (in[3].getOutput().getValueType() != ValueType.INT64) + throw new LanguageException("Fourth argument, seed, to sample() must be an integer value."); + if (in[2].getOutput().getValueType() != ValueType.BOOLEAN ) + throw new LanguageException("Third argument to sample() must either denote replacement policy (boolean) or seed (integer)."); + } + else if(in.length == 3) + { + checkNumParameters(3); + if (in[2].getOutput().getValueType() != ValueType.BOOLEAN + && in[2].getOutput().getValueType() != ValueType.INT64 ) + throw new LanguageException("Third argument to sample() must either denote replacement policy (boolean) or seed (integer)."); + } + + if ( check && in.length >= 3 + && isConstant(in[2]) + && in[2].getOutput().getValueType() == ValueType.BOOLEAN + && !((BooleanIdentifier)in[2]).getValue() ) + throw new LanguageException("Sample (size=" + ((ConstIdentifier)in[0]).getLongValue() + + ") larger than population (size=" + ((ConstIdentifier)in[1]).getLongValue() + + ") can only be generated with replacement."); + + // Output is a column vector + output.setDataType(DataType.MATRIX); + output.setValueType(ValueType.FP64); + + if ( isConstant(in[1]) ) + output.setDimensions(((ConstIdentifier)in[1]).getLongValue(), 1); + else + output.setDimensions(-1, 1); + setBlocksize(id.getBlocksize()); + + break; + } + case SEQ: + + //basic parameter validation + checkScalarParam(getFirstExpr()); + checkScalarParam(getSecondExpr()); + if ( getThirdExpr() != null ) { checkNumParameters(3); - checkMatrixParam(getFirstExpr()); - checkMatrixParam(getSecondExpr()); checkScalarParam(getThirdExpr()); - checkValueTypeParam(getThirdExpr(), ValueType.STRING); - if (id.getDim2() > 1 || id2.getDim1() > 1) { - raiseValidateError("Outer vector operations require a common dimension of one: " + - id.getDim1() + "x" + id.getDim2() + " o " + id2.getDim1() + "x" + id2.getDim2() + ".", - false); + } + else + checkNumParameters(2); + + // constant propagation (from, to, incr) + if( !conditional ) { + if( getFirstExpr() instanceof DataIdentifier && constVars.containsKey(((DataIdentifier)getFirstExpr()).getName()) ) + _args[0] = constVars.get(((DataIdentifier)getFirstExpr()).getName()); + if( getSecondExpr() instanceof DataIdentifier && constVars.containsKey(((DataIdentifier)getSecondExpr()).getName()) ) + _args[1] = constVars.get(((DataIdentifier)getSecondExpr()).getName()); + if( getThirdExpr()!=null && getThirdExpr() instanceof DataIdentifier && constVars.containsKey(((DataIdentifier)getThirdExpr()).getName()) ) + _args[2] = constVars.get(((DataIdentifier)getThirdExpr()).getName()); + } + + // check if dimensions can be inferred + long dim1=-1, dim2=1; + if ( isConstant(getFirstExpr()) && isConstant(getSecondExpr()) && (getThirdExpr() != null ? isConstant(getThirdExpr()) : true) ) { + double from, to, incr; + try { + from = getDoubleValue(getFirstExpr()); + to = getDoubleValue(getSecondExpr()); + + // Setup the value of increment + // default value: 1 if from <= to; -1 if from > to + if(getThirdExpr() == null) { + expandArguments(); + _args[2] = new DoubleIdentifier(((from > to) ? -1.0 : 1.0), this); + } + incr = getDoubleValue(getThirdExpr()); + + } + catch (LanguageException e) { + throw new LanguageException("Arguments for seq() must be numeric."); } - // set output characteristics - output.setDataType(id.getDataType()); - output.setDimensions(id.getDim1(), id2.getDim2()); - output.setBlocksize(id.getBlocksize()); - break; + if( (from > to) && (incr >= 0) ) + throw new LanguageException("Wrong sign for the increment in a call to seq()"); + + // Both end points of the range must included i.e., [from,to] both inclusive. + // Note that, "to" is included only if (to-from) is perfectly divisible by incr + // For example, seq(0,1,0.5) produces (0.0 0.5 1.0) whereas seq(0,1,0.6) produces only (0.0 0.6) but not (0.0 0.6 1.0) + dim1 = UtilFunctions.getSeqLength(from, to, incr); + } + output.setDataType(DataType.MATRIX); + output.setValueType(ValueType.FP64); + output.setDimensions(dim1, dim2); + output.setBlocksize(0); + break; + + case SOLVE: + checkNumParameters(2); + checkMatrixParam(getFirstExpr()); + checkMatrixParam(getSecondExpr()); + + if ( getSecondExpr().getOutput().dimsKnown() && !is1DMatrix(getSecondExpr()) ) + raiseValidateError("Second input to solve() must be a vector", conditional); + + if ( getFirstExpr().getOutput().dimsKnown() && getSecondExpr().getOutput().dimsKnown() && + getFirstExpr().getOutput().getDim1() != getSecondExpr().getOutput().getDim1() && + getFirstExpr().getOutput().getDim1() != getFirstExpr().getOutput().getDim2()) + raiseValidateError("Dimension mismatch in a call to solve()", conditional); + + output.setDataType(DataType.MATRIX); + output.setValueType(ValueType.FP64); + output.setDimensions(getFirstExpr().getOutput().getDim2(), 1); + output.setBlocksize(0); + break; + + case INVERSE: + checkNumParameters(1); + checkMatrixParam(getFirstExpr()); + + output.setDataType(DataType.MATRIX); + output.setValueType(ValueType.FP64); + + Identifier in = getFirstExpr().getOutput(); + if(in.dimsKnown() && in.getDim1() != in.getDim2()) + raiseValidateError("Input to inv() must be square matrix -- given: a " + in.getDim1() + "x" + in.getDim2() + " matrix.", conditional); + + output.setDimensions(in.getDim1(), in.getDim2()); + output.setBlocksize(in.getBlocksize()); + break; + + case CHOLESKY: + { + // A = L%*%t(L) where L is the lower triangular matrix + checkNumParameters(1); + checkMatrixParam(getFirstExpr()); + + output.setDataType(DataType.MATRIX); + output.setValueType(ValueType.FP64); + + Identifier inA = getFirstExpr().getOutput(); + if(inA.dimsKnown() && inA.getDim1() != inA.getDim2()) + raiseValidateError("Input to cholesky() must be square matrix -- given: a " + inA.getDim1() + "x" + inA.getDim2() + " matrix.", conditional); + + output.setDimensions(inA.getDim1(), inA.getDim2()); + output.setBlocksize(inA.getBlocksize()); + break; + } - case BIASADD: - case BIASMULT: { - Expression input = _args[0]; - Expression bias = _args[1]; - output.setDataType(DataType.MATRIX); - output.setValueType(ValueType.FP64); + case OUTER: + Identifier id2 = this.getSecondExpr().getOutput(); + + //check input types and characteristics + checkNumParameters(3); + checkMatrixParam(getFirstExpr()); + checkMatrixParam(getSecondExpr()); + checkScalarParam(getThirdExpr()); + checkValueTypeParam(getThirdExpr(), ValueType.STRING); + if( id.getDim2() > 1 || id2.getDim1()>1 ) { + raiseValidateError("Outer vector operations require a common dimension of one: " + + id.getDim1()+"x"+id.getDim2()+" o "+id2.getDim1()+"x"+id2.getDim2()+".", false); + } + + //set output characteristics + output.setDataType(id.getDataType()); + output.setDimensions(id.getDim1(), id2.getDim2()); + output.setBlocksize(id.getBlocksize()); + break; + + case BIASADD: + case BIASMULT: + { + Expression input = _args[0]; + Expression bias = _args[1]; + output.setDataType(DataType.MATRIX); + output.setValueType(ValueType.FP64); + output.setDimensions(input.getOutput().getDim1(), input.getOutput().getDim2()); + output.setBlocksize(input.getOutput().getBlocksize()); + checkMatrixParam(input); + checkMatrixParam(bias); + break; + } + case CONV2D: + case CONV2D_BACKWARD_FILTER: + case CONV2D_BACKWARD_DATA: + case MAX_POOL: + case AVG_POOL: + case MAX_POOL_BACKWARD: + case AVG_POOL_BACKWARD: + { + // At DML level: + // output = conv2d(input, filter, input_shape=[1, 3, 2, 2], filter_shape=[1, 3, 2, 2], + // strides=[1, 1], padding=[1,1]) + // + // Converted to following in constructor (only supported NCHW): + // output = conv2d(input, filter, stride1, stride2, padding1,padding2, + // input_shape1, input_shape2, input_shape3, input_shape4, + // filter_shape1, filter_shape2, filter_shape3, filter_shape4) + // + // Similarly, + // conv2d_backward_filter and conv2d_backward_data + Expression input = _args[0]; // For conv2d_backward_filter, this is input and for conv2d_backward_data, this is filter + + Expression input2 = null; + if(!(this.getOpCode() == Builtins.MAX_POOL || this.getOpCode() == Builtins.AVG_POOL)) { + input2 = _args[1]; // For conv2d_backward functions, this is dout + checkMatrixParam(input2); + } + output.setDataType(DataType.MATRIX); + output.setValueType(ValueType.FP64); + output.setBlocksize(input.getOutput().getBlocksize()); + + if(this.getOpCode() == Builtins.MAX_POOL_BACKWARD || this.getOpCode() == Builtins.AVG_POOL_BACKWARD) { output.setDimensions(input.getOutput().getDim1(), input.getOutput().getDim2()); - output.setBlocksize(input.getOutput().getBlocksize()); - checkMatrixParam(input); - checkMatrixParam(bias); - break; } - case CONV2D: - case CONV2D_BACKWARD_FILTER: - case CONV2D_BACKWARD_DATA: - case MAX_POOL: - case AVG_POOL: - case MAX_POOL_BACKWARD: - case AVG_POOL_BACKWARD: { - // At DML level: - // output = conv2d(input, filter, input_shape=[1, 3, 2, 2], filter_shape=[1, 3, - // 2, 2], - // strides=[1, 1], padding=[1,1]) - // - // Converted to following in constructor (only supported NCHW): - // output = conv2d(input, filter, stride1, stride2, padding1,padding2, - // input_shape1, input_shape2, input_shape3, input_shape4, - // filter_shape1, filter_shape2, filter_shape3, filter_shape4) - // - // Similarly, - // conv2d_backward_filter and conv2d_backward_data - Expression input = _args[0]; // For conv2d_backward_filter, this is input and for conv2d_backward_data, - // this is filter - - Expression input2 = null; - if (!(this.getOpCode() == Builtins.MAX_POOL || this.getOpCode() == Builtins.AVG_POOL)) { - input2 = _args[1]; // For conv2d_backward functions, this is dout - checkMatrixParam(input2); - } - output.setDataType(DataType.MATRIX); - output.setValueType(ValueType.FP64); - output.setBlocksize(input.getOutput().getBlocksize()); - - if (this.getOpCode() == Builtins.MAX_POOL_BACKWARD || this.getOpCode() == Builtins.AVG_POOL_BACKWARD) { - output.setDimensions(input.getOutput().getDim1(), input.getOutput().getDim2()); - } else { - // stride1, stride2, padding1, padding2, numImg, numChannels, imgSize, imgSize, - // filter_shape1=1, filter_shape2=1, filterSize/poolSize1, filterSize/poolSize1 - try { - int start = 2; - if (!(this.getOpCode() == Builtins.MAX_POOL || this.getOpCode() == Builtins.AVG_POOL)) { - start = 1; - } - long stride_h = (long) getDoubleValue(_args[start++]); - long stride_w = (long) getDoubleValue(_args[start++]); - long pad_h = (long) getDoubleValue(_args[start++]); - long pad_w = (long) getDoubleValue(_args[start++]); - long N = (long) getDoubleValue(_args[start++]); - long C = (long) getDoubleValue(_args[start++]); - long H = (long) getDoubleValue(_args[start++]); - long W = (long) getDoubleValue(_args[start++]); - long K = -1; - if (!(this.getOpCode() == Builtins.MAX_POOL || this.getOpCode() == Builtins.AVG_POOL)) { - K = (long) getDoubleValue(_args[start]); - } - start++; - start++; // Increment index for K and C - long R = (long) getDoubleValue(_args[start++]); - long S = (long) getDoubleValue(_args[start++]); - - if (this.getOpCode() == Builtins.CONV2D_BACKWARD_FILTER) { - output.setDimensions(K, C * R * S); - } else if (this.getOpCode() == Builtins.CONV2D_BACKWARD_DATA) { - output.setDimensions(N, C * H * W); - } else if (H > 0 && W > 0 && stride_h > 0 && stride_w > 0 && pad_h >= 0 && pad_w >= 0 && R > 0 - && S > 0) { - long P = DnnUtils.getP(H, R, stride_h, pad_h); - long Q = DnnUtils.getQ(W, S, stride_w, pad_w); - - // Try to set both rows and columns - if (this.getOpCode() == Builtins.CONV2D) - output.setDimensions(N, K * P * Q); - else if (this.getOpCode() == Builtins.MAX_POOL || this.getOpCode() == Builtins.AVG_POOL) - output.setDimensions(N, C * P * Q); - else - throw new LanguageException(""); - } else { - // Since columns cannot be computed, set only rows - if (this.getOpCode() == Builtins.CONV2D) - output.setDimensions(input.getOutput().getDim1(), -1); - else if (this.getOpCode() == Builtins.MAX_POOL || this.getOpCode() == Builtins.AVG_POOL) - output.setDimensions(input.getOutput().getDim1(), -1); - else - throw new LanguageException(""); - } - } catch (Exception e) { - output.setDimensions(-1, -1); // To make sure that output dimensions are not incorrect even if - // getDoubleValue doesnot return value + else { + // stride1, stride2, padding1, padding2, numImg, numChannels, imgSize, imgSize, + // filter_shape1=1, filter_shape2=1, filterSize/poolSize1, filterSize/poolSize1 + try { + int start = 2; + if(!(this.getOpCode() == Builtins.MAX_POOL || this.getOpCode() == Builtins.AVG_POOL)) { + start = 1; + } + long stride_h = (long) getDoubleValue(_args[start++]); + long stride_w = (long) getDoubleValue(_args[start++]); + long pad_h = (long) getDoubleValue(_args[start++]); + long pad_w = (long) getDoubleValue(_args[start++]); + long N = (long) getDoubleValue(_args[start++]); + long C = (long) getDoubleValue(_args[start++]); + long H = (long) getDoubleValue(_args[start++]); + long W = (long) getDoubleValue(_args[start++]); + long K = -1; + if(!(this.getOpCode() == Builtins.MAX_POOL || this.getOpCode() == Builtins.AVG_POOL)) { + K = (long) getDoubleValue(_args[start]); + } + start++; start++; // Increment index for K and C + long R = (long) getDoubleValue(_args[start++]); + long S = (long) getDoubleValue(_args[start++]); + + if(this.getOpCode() == Builtins.CONV2D_BACKWARD_FILTER) { + output.setDimensions(K, C*R*S); + } + else if(this.getOpCode() == Builtins.CONV2D_BACKWARD_DATA) { + output.setDimensions(N, C*H*W); + } + else if(H > 0 && W > 0 && stride_h > 0 && stride_w > 0 && pad_h >= 0 && pad_w >= 0 && R > 0 && S > 0) { + long P = DnnUtils.getP(H, R, stride_h, pad_h); + long Q = DnnUtils.getQ(W, S, stride_w, pad_w); + + // Try to set both rows and columns + if(this.getOpCode() == Builtins.CONV2D) + output.setDimensions(N, K*P*Q); + else if(this.getOpCode() == Builtins.MAX_POOL || this.getOpCode() == Builtins.AVG_POOL) + output.setDimensions(N, C*P*Q); + else + throw new LanguageException(""); + } + else { + // Since columns cannot be computed, set only rows + if(this.getOpCode() == Builtins.CONV2D) + output.setDimensions(input.getOutput().getDim1(), -1); + else if(this.getOpCode() == Builtins.MAX_POOL || this.getOpCode() == Builtins.AVG_POOL) + output.setDimensions(input.getOutput().getDim1(), -1); + else + throw new LanguageException(""); } } - checkMatrixParam(input); - if (input2 != null) - checkMatrixParam(input2); - break; + catch(Exception e) { + output.setDimensions(-1, -1); // To make sure that output dimensions are not incorrect even if getDoubleValue doesnot return value + } } - case TIME: - checkNumParameters(0); - // Output of TIME() is scalar and long - output.setDataType(DataType.SCALAR); - output.setValueType(ValueType.INT64); - output.setDimensions(0, 0); - output.setBlocksize(0); - break; - - case DROP_INVALID_TYPE: - case VALUE_SWAP: - case FRAME_ROW_REPLICATE: - checkNumParameters(2); - checkMatrixFrameParam(getFirstExpr()); - checkMatrixFrameParam(getSecondExpr()); - output.setDataType(DataType.FRAME); - output.setDimensions(id.getDim1(), id.getDim2()); - output.setBlocksize(id.getBlocksize()); - output.setValueType(ValueType.STRING); - break; - - case DROP_INVALID_LENGTH: + checkMatrixParam(input); + if(input2 != null) + checkMatrixParam(input2); + break; + } + case TIME: + checkNumParameters(0); + // Output of TIME() is scalar and long + output.setDataType(DataType.SCALAR); + output.setValueType(ValueType.INT64); + output.setDimensions(0, 0); + output.setBlocksize(0); + break; + + case DROP_INVALID_TYPE: + case VALUE_SWAP: + case FRAME_ROW_REPLICATE: + checkNumParameters(2); + checkMatrixFrameParam(getFirstExpr()); + checkMatrixFrameParam(getSecondExpr()); + output.setDataType(DataType.FRAME); + output.setDimensions(id.getDim1(), id.getDim2()); + output.setBlocksize (id.getBlocksize()); + output.setValueType(ValueType.STRING); + break; + + case DROP_INVALID_LENGTH: + checkNumParameters(2); + checkMatrixFrameParam(getFirstExpr()); + checkMatrixFrameParam(getSecondExpr()); + output.setDataType(DataType.FRAME); + output.setDimensions(id.getDim1(), id.getDim2()); + output.setBlocksize (id.getBlocksize()); + output.setValueType(id.getValueType()); + break; + case APPLY_SCHEMA: checkNumParameters(2); checkMatrixFrameParam(getFirstExpr()); checkMatrixFrameParam(getSecondExpr()); output.setDataType(DataType.FRAME); output.setDimensions(id.getDim1(), id.getDim2()); - output.setBlocksize(id.getBlocksize()); - output.setValueType(id.getValueType()); - break; - case APPLY_SCHEMA: - checkNumParameters(2); - checkMatrixFrameParam(getFirstExpr()); - checkMatrixFrameParam(getSecondExpr()); - output.setDataType(DataType.FRAME); + output.setBlocksize (id.getBlocksize()); + break; + case MAP: + checkNumParameters(getThirdExpr() != null ? 3 : 2); + checkMatrixFrameParam(getFirstExpr()); + checkScalarParam(getSecondExpr()); + if(getThirdExpr() != null) + checkScalarParam(getThirdExpr()); // margin + output.setDataType(DataType.FRAME); + if(_args[1].getText().contains("jaccardSim")) { + output.setDimensions(id.getDim1(), id.getDim1()); + output.setValueType(ValueType.FP64); + } + else { output.setDimensions(id.getDim1(), id.getDim2()); - output.setBlocksize(id.getBlocksize()); - break; - case MAP: - checkNumParameters(getThirdExpr() != null ? 3 : 2); - checkMatrixFrameParam(getFirstExpr()); - checkScalarParam(getSecondExpr()); - if (getThirdExpr() != null) - checkScalarParam(getThirdExpr()); // margin - output.setDataType(DataType.FRAME); - if (_args[1].getText().contains("jaccardSim")) { - output.setDimensions(id.getDim1(), id.getDim1()); - output.setValueType(ValueType.FP64); - } else { - output.setDimensions(id.getDim1(), id.getDim2()); - output.setValueType(ValueType.STRING); - } - break; - case LOCAL: - if (OptimizerUtils.ALLOW_SCRIPT_LEVEL_LOCAL_COMMAND) { - checkNumParameters(1); - checkMatrixParam(getFirstExpr()); - output.setDataType(DataType.MATRIX); - output.setDimensions(id.getDim1(), id.getDim2()); - output.setBlocksize(id.getBlocksize()); - output.setValueType(id.getValueType()); - } else - raiseValidateError("Local instruction not allowed in dml script"); - case COMPRESS: - case DECOMPRESS: - if (OptimizerUtils.ALLOW_SCRIPT_LEVEL_COMPRESS_COMMAND) { - checkNumParameters(1); - checkMatrixParam(getFirstExpr()); - output.setDataType(DataType.MATRIX); - output.setDimensions(id.getDim1(), id.getDim2()); - output.setBlocksize(id.getBlocksize()); - output.setValueType(id.getValueType()); - } else - raiseValidateError("Compress/DeCompress instruction not allowed in dml script"); - break; - case ROW_COUNT_DISTINCT: + output.setValueType(ValueType.STRING); + } + break; + case LOCAL: + if(OptimizerUtils.ALLOW_SCRIPT_LEVEL_LOCAL_COMMAND){ checkNumParameters(1); checkMatrixParam(getFirstExpr()); output.setDataType(DataType.MATRIX); - output.setDimensions(id.getDim1(), 1); - output.setBlocksize(id.getBlocksize()); - output.setValueType(ValueType.INT64); - output.setNnz(id.getDim1()); - break; - - case COL_COUNT_DISTINCT: + output.setDimensions(id.getDim1(), id.getDim2()); + output.setBlocksize (id.getBlocksize()); + output.setValueType(id.getValueType()); + } + else + raiseValidateError("Local instruction not allowed in dml script"); + case COMPRESS: + case DECOMPRESS: + if(OptimizerUtils.ALLOW_SCRIPT_LEVEL_COMPRESS_COMMAND){ checkNumParameters(1); checkMatrixParam(getFirstExpr()); output.setDataType(DataType.MATRIX); - output.setDimensions(1, id.getDim2()); - output.setBlocksize(id.getBlocksize()); - output.setValueType(ValueType.INT64); - output.setNnz(id.getDim2()); - break; - - default: - if (isMathFunction()) { - checkMathFunctionParam(); - // unary operations - if (getSecondExpr() == null) { - output.setDataType(id.getDataType()); - output.setValueType((output.getDataType() == DataType.SCALAR - && getOpCode() == Builtins.ABS) ? id.getValueType() : ValueType.FP64); - output.setDimensions(id.getDim1(), id.getDim2()); - output.setBlocksize(id.getBlocksize()); - } - // binary operations - else { - setBinaryOutputProperties(output); - // override computed value type for special cases - if (getOpCode() == Builtins.LOG) - output.setValueType(ValueType.FP64); - } - } else { - // always unconditional (because unsupported operation) - Builtins op = getOpCode(); - if (op == Builtins.EIGEN || op == Builtins.LU || op == Builtins.QR || op == Builtins.SVD - || op == Builtins.LSTM || op == Builtins.LSTM_BACKWARD - || op == Builtins.BATCH_NORM2D || op == Builtins.BATCH_NORM2D_BACKWARD) - raiseValidateError("Function " + op + " needs to be called with multi-return assignment.", - false, LanguageErrorCodes.INVALID_PARAMETERS); - else - raiseValidateError("Unsupported function " + op, false, LanguageErrorCodes.INVALID_PARAMETERS); + output.setDimensions(id.getDim1(), id.getDim2()); + output.setBlocksize (id.getBlocksize()); + output.setValueType(id.getValueType()); + } + else + raiseValidateError("Compress/DeCompress instruction not allowed in dml script"); + break; + case ROW_COUNT_DISTINCT: + checkNumParameters(1); + checkMatrixParam(getFirstExpr()); + output.setDataType(DataType.MATRIX); + output.setDimensions(id.getDim1(), 1); + output.setBlocksize (id.getBlocksize()); + output.setValueType(ValueType.INT64); + output.setNnz(id.getDim1()); + break; + + case COL_COUNT_DISTINCT: + checkNumParameters(1); + checkMatrixParam(getFirstExpr()); + output.setDataType(DataType.MATRIX); + output.setDimensions(1, id.getDim2()); + output.setBlocksize (id.getBlocksize()); + output.setValueType(ValueType.INT64); + output.setNnz(id.getDim2()); + break; + + default: + if( isMathFunction() ) { + checkMathFunctionParam(); + //unary operations + if( getSecondExpr() == null ) { + output.setDataType(id.getDataType()); + output.setValueType((output.getDataType()==DataType.SCALAR + && getOpCode()==Builtins.ABS)?id.getValueType():ValueType.FP64 ); + output.setDimensions(id.getDim1(), id.getDim2()); + output.setBlocksize(id.getBlocksize()); } + //binary operations + else { + setBinaryOutputProperties(output); + // override computed value type for special cases + if( getOpCode() == Builtins.LOG ) + output.setValueType(ValueType.FP64); + } + } + else { + // always unconditional (because unsupported operation) + Builtins op = getOpCode(); + if( op==Builtins.EIGEN || op==Builtins.LU || op==Builtins.QR || op==Builtins.SVD + || op==Builtins.LSTM || op==Builtins.LSTM_BACKWARD + || op==Builtins.BATCH_NORM2D || op==Builtins.BATCH_NORM2D_BACKWARD) + raiseValidateError("Function "+op+" needs to be called with multi-return assignment.", false, LanguageErrorCodes.INVALID_PARAMETERS); + else + raiseValidateError("Unsupported function "+op, false, LanguageErrorCodes.INVALID_PARAMETERS); + } } } private void setBinaryOutputProperties(DataIdentifier output) { DataType dt1 = getFirstExpr().getOutput().getDataType(); DataType dt2 = getSecondExpr().getOutput().getDataType(); - DataType dtOut = (dt1 == DataType.MATRIX || dt2 == DataType.MATRIX) ? DataType.MATRIX : DataType.SCALAR; - if (dt1 == DataType.MATRIX && dt2 == DataType.MATRIX) + DataType dtOut = (dt1==DataType.MATRIX || dt2==DataType.MATRIX) ? + DataType.MATRIX : DataType.SCALAR; + if( dt1==DataType.MATRIX && dt2==DataType.MATRIX ) checkMatchingDimensions(getFirstExpr(), getSecondExpr(), true); MatrixCharacteristics dims = getBinaryMatrixCharacteristics(getFirstExpr(), getSecondExpr()); output.setDataType(dtOut); - output.setValueType( - dtOut == DataType.MATRIX ? ValueType.FP64 : computeValueType(getFirstExpr(), getSecondExpr(), true)); + output.setValueType(dtOut==DataType.MATRIX ? ValueType.FP64 : + computeValueType(getFirstExpr(), getSecondExpr(), true)); output.setDimensions(dims.getRows(), dims.getCols()); - output.setBlocksize(dims.getBlocksize()); + output.setBlocksize (dims.getBlocksize()); } - + private void setTernaryOutputProperties(DataIdentifier output, boolean conditional) { DataType dt1 = getFirstExpr().getOutput().getDataType(); DataType dt2 = getSecondExpr().getOutput().getDataType(); DataType dt3 = getThirdExpr().getOutput().getDataType(); - DataType dtOut = (dt1.isMatrix() || dt2.isMatrix() || dt3.isMatrix()) ? DataType.MATRIX : DataType.SCALAR; - if (dt1 == DataType.MATRIX && dt2 == DataType.MATRIX) + DataType dtOut = (dt1.isMatrix() || dt2.isMatrix() || dt3.isMatrix()) ? + DataType.MATRIX : DataType.SCALAR; + if( dt1==DataType.MATRIX && dt2==DataType.MATRIX ) checkMatchingDimensions(getFirstExpr(), getSecondExpr(), false, conditional); - if (dt1 == DataType.MATRIX && dt3 == DataType.MATRIX) + if( dt1==DataType.MATRIX && dt3==DataType.MATRIX ) checkMatchingDimensions(getFirstExpr(), getThirdExpr(), false, conditional); - if (dt2 == DataType.MATRIX && dt3 == DataType.MATRIX) + if( dt2==DataType.MATRIX && dt3==DataType.MATRIX ) checkMatchingDimensions(getSecondExpr(), getThirdExpr(), false, conditional); MatrixCharacteristics dims1 = getBinaryMatrixCharacteristics(getFirstExpr(), getSecondExpr()); MatrixCharacteristics dims2 = getBinaryMatrixCharacteristics(getSecondExpr(), getThirdExpr()); output.setDataType(dtOut); - output.setValueType( - dtOut == DataType.MATRIX ? ValueType.FP64 : computeValueType(getSecondExpr(), getThirdExpr(), true)); + output.setValueType(dtOut==DataType.MATRIX ? ValueType.FP64 : + computeValueType(getSecondExpr(), getThirdExpr(), true)); output.setDimensions(Math.max(dims1.getRows(), dims2.getRows()), Math.max(dims1.getCols(), dims2.getCols())); output.setBlocksize(Math.max(dims1.getBlocksize(), dims2.getBlocksize())); } private void setNaryOutputProperties(DataIdentifier output) { DataType dt = Arrays.stream(getAllExpr()).allMatch( - e -> e.getOutput().getDataType().isScalar()) ? DataType.SCALAR : DataType.MATRIX; + e -> e.getOutput().getDataType().isScalar()) ? DataType.SCALAR : DataType.MATRIX; Expression firstM = dt.isMatrix() ? Arrays.stream(getAllExpr()).filter( - e -> e.getOutput().getDataType().isMatrix()).findFirst().get() : null; + e -> e.getOutput().getDataType().isMatrix()).findFirst().get() : null; ValueType vt = dt.isMatrix() ? ValueType.FP64 : ValueType.INT64; - for (Expression e : getAllExpr()) { + for( Expression e : getAllExpr() ) { vt = computeValueType(e, e.getOutput().getValueType(), vt, true); - if (e.getOutput().getDataType().isMatrix()) + if( e.getOutput().getDataType().isMatrix() ) checkMatchingDimensions(firstM, e, true); } output.setDataType(dt); output.setValueType(vt); output.setDimensions(dt.isMatrix() ? firstM.getOutput().getDim1() : 0, - dt.isMatrix() ? firstM.getOutput().getDim2() : 0); - output.setBlocksize(dt.isMatrix() ? firstM.getOutput().getBlocksize() : 0); + dt.isMatrix() ? firstM.getOutput().getDim2() : 0); + output.setBlocksize (dt.isMatrix() ? firstM.getOutput().getBlocksize() : 0); } - + private void expandArguments() { - - if (_args == null) { + + if ( _args == null ) { _args = new Expression[1]; return; } - Expression[] temp = _args.clone(); + Expression [] temp = _args.clone(); _args = new Expression[_args.length + 1]; System.arraycopy(temp, 0, _args, 0, temp.length); } - + @Override public boolean multipleReturns() { return _opcode.isMultiReturn(); } private static boolean isConstant(Expression expr) { - return (expr != null && expr instanceof ConstIdentifier); + return ( expr != null && expr instanceof ConstIdentifier ); } - + private static double getDoubleValue(Expression expr) { - if (expr instanceof DoubleIdentifier) - return ((DoubleIdentifier) expr).getValue(); - else if (expr instanceof IntIdentifier) - return ((IntIdentifier) expr).getValue(); + if ( expr instanceof DoubleIdentifier ) + return ((DoubleIdentifier)expr).getValue(); + else if ( expr instanceof IntIdentifier) + return ((IntIdentifier)expr).getValue(); else throw new LanguageException("Expecting a numeric value."); } - + private boolean isMathFunction() { switch (this.getOpCode()) { - case COS: - case SIN: - case TAN: - case ACOS: - case ASIN: - case ATAN: - case COSH: - case SINH: - case TANH: - case SIGN: - case SQRT: - case ABS: - case LOG: - case EXP: - case ROUND: - case CEIL: - case FLOOR: - case MEDIAN: - case XOR: - case BITWAND: - case BITWOR: - case BITWXOR: - case BITWSHIFTL: - case BITWSHIFTR: - return true; - default: - return false; + case COS: + case SIN: + case TAN: + case ACOS: + case ASIN: + case ATAN: + case COSH: + case SINH: + case TANH: + case SIGN: + case SQRT: + case ABS: + case LOG: + case EXP: + case ROUND: + case CEIL: + case FLOOR: + case MEDIAN: + case XOR: + case BITWAND: + case BITWOR: + case BITWXOR: + case BITWSHIFTL: + case BITWSHIFTR: + return true; + default: + return false; } } private void checkMathFunctionParam() { switch (this.getOpCode()) { - case COS: - case SIN: - case TAN: - case ACOS: - case ASIN: - case ATAN: - case COSH: - case SINH: - case TANH: - case SIGN: - case SQRT: - case ABS: - case EXP: - case ROUND: - case CEIL: - case FLOOR: - case MEDIAN: - checkNumParameters(1); - break; - case LOG: - if (getSecondExpr() != null) { - checkNumParameters(2); - } else { - checkNumParameters(1); - } - break; - default: - // always unconditional - raiseValidateError("Unknown math function " + this.getOpCode(), false); + case COS: + case SIN: + case TAN: + case ACOS: + case ASIN: + case ATAN: + case COSH: + case SINH: + case TANH: + case SIGN: + case SQRT: + case ABS: + case EXP: + case ROUND: + case CEIL: + case FLOOR: + case MEDIAN: + checkNumParameters(1); + break; + case LOG: + if (getSecondExpr() != null) { + checkNumParameters(2); + } + else { + checkNumParameters(1); + } + break; + default: + //always unconditional + raiseValidateError("Unknown math function "+ this.getOpCode(), false); } } @@ -1951,11 +1936,11 @@ public String toString() { // third part of expression IS NOT a variable -- it is the OP to be applied public VariableSet variablesRead() { VariableSet result = new VariableSet(); - - for (int i = 0; i < _args.length; i++) { + + for(int i=0; i<_args.length; i++) { result.addVariables(_args[i].variablesRead()); } - + return result; } @@ -1966,14 +1951,13 @@ public VariableSet variablesUpdated() { return result; } - protected void checkNumParameters(int count) { // always unconditional + protected void checkNumParameters(int count) { //always unconditional if (getFirstExpr() == null && _args.length > 0) { raiseValidateError("Missing argument for function " + this.getOpCode(), false, LanguageErrorCodes.INVALID_PARAMETERS); } - // Not sure the rationale for the first two if loops, but will keep them for - // backward compatibility + // Not sure the rationale for the first two if loops, but will keep them for backward compatibility if (((count == 1) && (getSecondExpr() != null || getThirdExpr() != null)) || ((count == 2) && (getThirdExpr() != null))) { raiseValidateError("Invalid number of arguments for function " + this.getOpCode().toString().toLowerCase() @@ -1982,7 +1966,7 @@ protected void checkNumParameters(int count) { // always unconditional || ((count == 3) && (getSecondExpr() == null || getThirdExpr() == null))) { raiseValidateError("Missing argument for function " + this.getOpCode(), false, LanguageErrorCodes.INVALID_PARAMETERS); - } else if (count > 0 && (_args == null || _args.length < count)) { + } else if(count > 0 && (_args == null || _args.length < count)) { raiseValidateError("Missing argument for function " + this.getOpCode(), false, LanguageErrorCodes.INVALID_PARAMETERS); } else if (count == 0 && (_args.length > 0 @@ -1998,7 +1982,7 @@ protected void checkMatrixParam(Expression e) { this.getOpCode().toString().toLowerCase() + "().", false); } } - + protected void checkMatrixTensorParam(Expression e) { if (e.getOutput().getDataType() != DataType.MATRIX) { // Param is not a matrix @@ -2011,145 +1995,137 @@ protected void checkMatrixTensorParam(Expression e) { } } - protected void checkDataTypeParam(Expression e, DataType... dt) { // always unconditional - if (!ArrayUtils.contains(dt, e.getOutput().getDataType())) - raiseValidateError("Non-matching expected data type for function " + getOpCode(), false, - LanguageErrorCodes.UNSUPPORTED_PARAMETERS); + protected void checkDataTypeParam(Expression e, DataType... dt) { //always unconditional + if( !ArrayUtils.contains(dt, e.getOutput().getDataType()) ) + raiseValidateError("Non-matching expected data type for function "+ getOpCode(), false, LanguageErrorCodes.UNSUPPORTED_PARAMETERS); } - protected void checkMatrixFrameParam(Expression e) { // always unconditional + protected void checkMatrixFrameParam(Expression e) { //always unconditional if (e.getOutput().getDataType() != DataType.MATRIX && e.getOutput().getDataType() != DataType.FRAME) { - raiseValidateError("Expecting matrix or frame parameter for function " + getOpCode(), false, - LanguageErrorCodes.UNSUPPORTED_PARAMETERS); + raiseValidateError("Expecting matrix or frame parameter for function "+ getOpCode(), false, LanguageErrorCodes.UNSUPPORTED_PARAMETERS); } } - - protected void checkMatrixScalarParam(Expression e) { // always unconditional + + protected void checkMatrixScalarParam(Expression e) { //always unconditional if (e.getOutput().getDataType() != DataType.MATRIX && e.getOutput().getDataType() != DataType.SCALAR) { - raiseValidateError("Expecting matrix or scalar parameter for function " + getOpCode(), false, - LanguageErrorCodes.UNSUPPORTED_PARAMETERS); + raiseValidateError("Expecting matrix or scalar parameter for function "+ getOpCode(), false, LanguageErrorCodes.UNSUPPORTED_PARAMETERS); } } - - private void checkScalarParam(Expression e) { // always unconditional + + private void checkScalarParam(Expression e) { //always unconditional if (e.getOutput().getDataType() != DataType.SCALAR) { - raiseValidateError("Expecting scalar parameter for function " + getOpCode(), false, - LanguageErrorCodes.UNSUPPORTED_PARAMETERS); + raiseValidateError("Expecting scalar parameter for function " + getOpCode(), false, LanguageErrorCodes.UNSUPPORTED_PARAMETERS); } } - - private void checkListParam(Expression e) { // always unconditional + + private void checkListParam(Expression e) { //always unconditional if (e.getOutput().getDataType() != DataType.LIST) { - raiseValidateError("Expecting scalar parameter for function " + getOpCode(), false, - LanguageErrorCodes.UNSUPPORTED_PARAMETERS); + raiseValidateError("Expecting scalar parameter for function " + getOpCode(), false, LanguageErrorCodes.UNSUPPORTED_PARAMETERS); } } - + @SuppressWarnings("unused") - private void checkScalarFrameParam(Expression e) { // always unconditional + private void checkScalarFrameParam(Expression e) { //always unconditional if (e.getOutput().getDataType() != DataType.SCALAR && e.getOutput().getDataType() != DataType.FRAME) { - raiseValidateError("Expecting scalar parameter for function " + getOpCode(), false, - LanguageErrorCodes.UNSUPPORTED_PARAMETERS); + raiseValidateError("Expecting scalar parameter for function " + getOpCode(), false, LanguageErrorCodes.UNSUPPORTED_PARAMETERS); } } - private void checkValueTypeParam(Expression e, ValueType vt) { // always unconditional + private void checkValueTypeParam(Expression e, ValueType vt) { //always unconditional if (e.getOutput().getValueType() != vt) { - raiseValidateError("Expecting parameter of different value type " + this.getOpCode(), false, - LanguageErrorCodes.UNSUPPORTED_PARAMETERS); + raiseValidateError("Expecting parameter of different value type " + this.getOpCode(), false, LanguageErrorCodes.UNSUPPORTED_PARAMETERS); } } - - protected void checkStringOrDataIdentifier(Expression e) { // always unconditional - if (!(e.getOutput().getDataType().isScalar() && e.getOutput().getValueType() == ValueType.STRING) - && !(e instanceof DataIdentifier && !(e instanceof IndexedIdentifier))) { - raiseValidateError("Expecting variable name or data identifier " + getOpCode(), false, - LanguageErrorCodes.UNSUPPORTED_PARAMETERS); + + protected void checkStringOrDataIdentifier(Expression e) { //always unconditional + if( !(e.getOutput().getDataType().isScalar() && e.getOutput().getValueType()==ValueType.STRING) + && !(e instanceof DataIdentifier && !(e instanceof IndexedIdentifier)) ) { + raiseValidateError("Expecting variable name or data identifier "+ getOpCode(), false, LanguageErrorCodes.UNSUPPORTED_PARAMETERS); } } - + private static boolean is1DMatrix(Expression e) { - return (e.getOutput().getDim1() == 1 || e.getOutput().getDim2() == 1); + return (e.getOutput().getDim1() == 1 || e.getOutput().getDim2() == 1 ); } - + private static boolean dimsKnown(Expression e) { return (e.getOutput().getDim1() != -1 && e.getOutput().getDim2() != -1); } - - private void check1DMatrixParam(Expression e) { // always unconditional + + private void check1DMatrixParam(Expression e) { //always unconditional checkMatrixParam(e); - - // throw an exception, when e's output is NOT a one-dimensional matrix - // the check must be performed only when the dimensions are known at compilation - // time - if (dimsKnown(e) && !is1DMatrix(e)) { + + // throw an exception, when e's output is NOT a one-dimensional matrix + // the check must be performed only when the dimensions are known at compilation time + if ( dimsKnown(e) && !is1DMatrix(e)) { raiseValidateError("Expecting one-dimensional matrix parameter for function " - + this.getOpCode(), false, LanguageErrorCodes.UNSUPPORTED_PARAMETERS); + + this.getOpCode(), false, LanguageErrorCodes.UNSUPPORTED_PARAMETERS); } } private void checkMatchingDimensions(Expression expr1, Expression expr2) { checkMatchingDimensions(expr1, expr2, false); } - + private void checkMatchingDimensions(Expression expr1, Expression expr2, boolean allowsMV) { checkMatchingDimensions(expr1, expr2, allowsMV, false); } - - private void checkMatchingDimensions(Expression expr1, Expression expr2, boolean allowsMV, boolean conditional) { + + private void checkMatchingDimensions(Expression expr1, Expression expr2, boolean allowsMV, boolean conditional) + { if (expr1 != null && expr2 != null) { - + // if any matrix has unknown dimensions, simply return - if (expr1.getOutput().getDim1() == -1 || expr2.getOutput().getDim1() == -1 - || expr1.getOutput().getDim2() == -1 || expr2.getOutput().getDim2() == -1) { + if( expr1.getOutput().getDim1() == -1 || expr2.getOutput().getDim1() == -1 + ||expr1.getOutput().getDim2() == -1 || expr2.getOutput().getDim2() == -1 ) + { return; - } else if ((!allowsMV && expr1.getOutput().getDim1() != expr2.getOutput().getDim1()) - || (allowsMV && expr1.getOutput().getDim1() != expr2.getOutput().getDim1() - && expr2.getOutput().getDim1() != 1) - || (!allowsMV && expr1.getOutput().getDim2() != expr2.getOutput().getDim2()) - || (allowsMV && expr1.getOutput().getDim2() != expr2.getOutput().getDim2() - && expr2.getOutput().getDim2() != 1)) { + } + else if( (!allowsMV && expr1.getOutput().getDim1() != expr2.getOutput().getDim1()) + || (allowsMV && expr1.getOutput().getDim1() != expr2.getOutput().getDim1() && expr2.getOutput().getDim1() != 1) + || (!allowsMV && expr1.getOutput().getDim2() != expr2.getOutput().getDim2()) + || (allowsMV && expr1.getOutput().getDim2() != expr2.getOutput().getDim2() && expr2.getOutput().getDim2() != 1) ) + { raiseValidateError("Mismatch in matrix dimensions of parameters for function " + this.getOpCode(), conditional, LanguageErrorCodes.INVALID_PARAMETERS); } } } - - private void checkMatchingDimensionsQuantile() { + + private void checkMatchingDimensionsQuantile() + { if (getFirstExpr().getOutput().getDim1() != getSecondExpr().getOutput().getDim1()) { raiseValidateError("Mismatch in matrix dimensions for " + this.getOpCode(), false, LanguageErrorCodes.INVALID_PARAMETERS); } } - public static BuiltinFunctionExpression getBuiltinFunctionExpression(ParserRuleContext ctx, - String functionName, ArrayList paramExprsPassed, String filename) { - + public static BuiltinFunctionExpression getBuiltinFunctionExpression(ParserRuleContext ctx, + String functionName, ArrayList paramExprsPassed, String filename) { + if (functionName == null || paramExprsPassed == null) return null; - + // check if the function name is built-in function - // (assign built-in function op if function is built-in - - return (Builtins.contains(functionName, false, false) - && (paramExprsPassed.stream().anyMatch(p -> p.getName() == null) // at least one unnamed - || paramExprsPassed.size() == 0)) - ? new BuiltinFunctionExpression(ctx, Builtins.get(functionName), paramExprsPassed, - filename) - : null; + // (assign built-in function op if function is built-in + + + return (Builtins.contains(functionName, false, false) + && (paramExprsPassed.stream().anyMatch(p -> p.getName()==null) //at least one unnamed + || paramExprsPassed.size() == 0)) ? + new BuiltinFunctionExpression(ctx, Builtins.get(functionName), paramExprsPassed, filename) : null; } - + /** - * Convert a value type (double, int, or boolean) to a built-in function - * operator. + * Convert a value type (double, int, or boolean) to a built-in function operator. * - * @param vt Value type ({@code ValueType.DOUBLE}, {@code ValueType.INT}, or - * {@code ValueType.BOOLEAN}). + * @param vt Value type ({@code ValueType.DOUBLE}, {@code ValueType.INT}, or {@code ValueType.BOOLEAN}). * @return Built-in function operator ({@code Builtins.AS_DOUBLE}, - * {@code Builtins.AS_INT}, or {@code Builtins.AS_BOOLEAN}). + * {@code Builtins.AS_INT}, or {@code Builtins.AS_BOOLEAN}). */ - public static Builtins getValueTypeCastOperator(ValueType vt) { - switch (vt) { + public static Builtins getValueTypeCastOperator( ValueType vt ) { + switch( vt ) + { case FP64: return Builtins.CAST_AS_DOUBLE; case INT64: @@ -2157,7 +2133,7 @@ public static Builtins getValueTypeCastOperator(ValueType vt) { case BOOLEAN: return Builtins.CAST_AS_BOOLEAN; default: - throw new LanguageException("No cast for value type " + vt); + throw new LanguageException("No cast for value type "+vt); } } -} +} \ No newline at end of file From ba7bd194aa46360549e67d4f27aef9318ca74546 Mon Sep 17 00:00:00 2001 From: Frederic Zoepffel Date: Thu, 8 Feb 2024 23:53:39 +0100 Subject: [PATCH 055/133] reversed formatting of CPInstructionParser --- .../instructions/CPInstructionParser.java | 525 +++++++++--------- 1 file changed, 262 insertions(+), 263 deletions(-) diff --git a/src/main/java/org/apache/sysds/runtime/instructions/CPInstructionParser.java b/src/main/java/org/apache/sysds/runtime/instructions/CPInstructionParser.java index a8702a39b1b..58158991f34 100644 --- a/src/main/java/org/apache/sysds/runtime/instructions/CPInstructionParser.java +++ b/src/main/java/org/apache/sysds/runtime/instructions/CPInstructionParser.java @@ -82,228 +82,228 @@ public class CPInstructionParser extends InstructionParser { public static final HashMap String2CPInstructionType; static { String2CPInstructionType = new HashMap<>(); - String2CPInstructionType.put("ba+*", CPType.AggregateBinary); - String2CPInstructionType.put("tak+*", CPType.AggregateTernary); - String2CPInstructionType.put("tack+*", CPType.AggregateTernary); - - String2CPInstructionType.put("uak+", CPType.AggregateUnary); - String2CPInstructionType.put("uark+", CPType.AggregateUnary); - String2CPInstructionType.put("uack+", CPType.AggregateUnary); - String2CPInstructionType.put("uasqk+", CPType.AggregateUnary); - String2CPInstructionType.put("uarsqk+", CPType.AggregateUnary); - String2CPInstructionType.put("uacsqk+", CPType.AggregateUnary); - String2CPInstructionType.put("uamean", CPType.AggregateUnary); - String2CPInstructionType.put("uarmean", CPType.AggregateUnary); - String2CPInstructionType.put("uacmean", CPType.AggregateUnary); - String2CPInstructionType.put("uavar", CPType.AggregateUnary); - String2CPInstructionType.put("uarvar", CPType.AggregateUnary); - String2CPInstructionType.put("uacvar", CPType.AggregateUnary); - String2CPInstructionType.put("uamax", CPType.AggregateUnary); - String2CPInstructionType.put("uarmax", CPType.AggregateUnary); - String2CPInstructionType.put("uarimax", CPType.AggregateUnary); - String2CPInstructionType.put("uacmax", CPType.AggregateUnary); - String2CPInstructionType.put("uamin", CPType.AggregateUnary); - String2CPInstructionType.put("uarmin", CPType.AggregateUnary); - String2CPInstructionType.put("uarimin", CPType.AggregateUnary); - String2CPInstructionType.put("uacmin", CPType.AggregateUnary); - String2CPInstructionType.put("ua+", CPType.AggregateUnary); - String2CPInstructionType.put("uar+", CPType.AggregateUnary); - String2CPInstructionType.put("uac+", CPType.AggregateUnary); - String2CPInstructionType.put("ua*", CPType.AggregateUnary); - String2CPInstructionType.put("uar*", CPType.AggregateUnary); - String2CPInstructionType.put("uac*", CPType.AggregateUnary); - String2CPInstructionType.put("uatrace", CPType.AggregateUnary); - String2CPInstructionType.put("uaktrace", CPType.AggregateUnary); - String2CPInstructionType.put("nrow", CPType.AggregateUnary); - String2CPInstructionType.put("ncol", CPType.AggregateUnary); - String2CPInstructionType.put("length", CPType.AggregateUnary); - String2CPInstructionType.put("exists", CPType.AggregateUnary); - String2CPInstructionType.put("lineage", CPType.AggregateUnary); - String2CPInstructionType.put("uacd", CPType.AggregateUnary); - String2CPInstructionType.put("uacdr", CPType.AggregateUnary); - String2CPInstructionType.put("uacdc", CPType.AggregateUnary); - String2CPInstructionType.put("uacdap", CPType.AggregateUnary); - String2CPInstructionType.put("uacdapr", CPType.AggregateUnary); - String2CPInstructionType.put("uacdapc", CPType.AggregateUnary); - String2CPInstructionType.put("unique", CPType.AggregateUnary); - String2CPInstructionType.put("uniquer", CPType.AggregateUnary); - String2CPInstructionType.put("uniquec", CPType.AggregateUnary); - - String2CPInstructionType.put("uaggouterchain", CPType.UaggOuterChain); - - // Arithmetic Instruction Opcodes - String2CPInstructionType.put("+", CPType.Binary); - String2CPInstructionType.put("-", CPType.Binary); - String2CPInstructionType.put("*", CPType.Binary); - String2CPInstructionType.put("/", CPType.Binary); - String2CPInstructionType.put("%%", CPType.Binary); - String2CPInstructionType.put("%/%", CPType.Binary); - String2CPInstructionType.put("^", CPType.Binary); - String2CPInstructionType.put("1-*", CPType.Binary); // special * case - String2CPInstructionType.put("^2", CPType.Binary); // special ^ case - String2CPInstructionType.put("*2", CPType.Binary); // special * case - String2CPInstructionType.put("-nz", CPType.Binary); // special - case - - // Boolean Instruction Opcodes - String2CPInstructionType.put("&&", CPType.Binary); - String2CPInstructionType.put("||", CPType.Binary); - String2CPInstructionType.put("xor", CPType.Binary); - String2CPInstructionType.put("bitwAnd", CPType.Binary); - String2CPInstructionType.put("bitwOr", CPType.Binary); - String2CPInstructionType.put("bitwXor", CPType.Binary); - String2CPInstructionType.put("bitwShiftL", CPType.Binary); - String2CPInstructionType.put("bitwShiftR", CPType.Binary); - String2CPInstructionType.put("!", CPType.Unary); - - // Relational Instruction Opcodes - String2CPInstructionType.put("==", CPType.Binary); - String2CPInstructionType.put("!=", CPType.Binary); - String2CPInstructionType.put("<", CPType.Binary); - String2CPInstructionType.put(">", CPType.Binary); - String2CPInstructionType.put("<=", CPType.Binary); - String2CPInstructionType.put(">=", CPType.Binary); - - // Builtin Instruction Opcodes - String2CPInstructionType.put("log", CPType.Builtin); - String2CPInstructionType.put("log_nz", CPType.Builtin); - - String2CPInstructionType.put("solve", CPType.Binary); - String2CPInstructionType.put("max", CPType.Binary); - String2CPInstructionType.put("min", CPType.Binary); - String2CPInstructionType.put("dropInvalidType", CPType.Binary); - String2CPInstructionType.put("dropInvalidLength", CPType.Binary); - String2CPInstructionType.put("freplicate", CPType.Binary); - String2CPInstructionType.put("valueSwap", CPType.Binary); - String2CPInstructionType.put("applySchema", CPType.Binary); - String2CPInstructionType.put("_map", CPType.Ternary); // _map represents the operation map - - String2CPInstructionType.put("nmax", CPType.BuiltinNary); - String2CPInstructionType.put("nmin", CPType.BuiltinNary); - String2CPInstructionType.put("n+", CPType.BuiltinNary); - - String2CPInstructionType.put("exp", CPType.Unary); - String2CPInstructionType.put("abs", CPType.Unary); - String2CPInstructionType.put("sin", CPType.Unary); - String2CPInstructionType.put("cos", CPType.Unary); - String2CPInstructionType.put("tan", CPType.Unary); - String2CPInstructionType.put("sinh", CPType.Unary); - String2CPInstructionType.put("cosh", CPType.Unary); - String2CPInstructionType.put("tanh", CPType.Unary); - String2CPInstructionType.put("asin", CPType.Unary); - String2CPInstructionType.put("acos", CPType.Unary); - String2CPInstructionType.put("atan", CPType.Unary); - String2CPInstructionType.put("sign", CPType.Unary); - String2CPInstructionType.put("sqrt", CPType.Unary); - String2CPInstructionType.put("plogp", CPType.Unary); - String2CPInstructionType.put("print", CPType.Unary); - String2CPInstructionType.put("assert", CPType.Unary); - String2CPInstructionType.put("round", CPType.Unary); - String2CPInstructionType.put("ceil", CPType.Unary); - String2CPInstructionType.put("floor", CPType.Unary); - String2CPInstructionType.put("ucumk+", CPType.Unary); - String2CPInstructionType.put("ucum*", CPType.Unary); - String2CPInstructionType.put("ucumk+*", CPType.Unary); - String2CPInstructionType.put("ucummin", CPType.Unary); - String2CPInstructionType.put("ucummax", CPType.Unary); - String2CPInstructionType.put("stop", CPType.Unary); - String2CPInstructionType.put("inverse", CPType.Unary); - String2CPInstructionType.put("cholesky", CPType.Unary); - String2CPInstructionType.put("sprop", CPType.Unary); - String2CPInstructionType.put("sigmoid", CPType.Unary); - String2CPInstructionType.put("typeOf", CPType.Unary); - String2CPInstructionType.put("detectSchema", CPType.Unary); - String2CPInstructionType.put("colnames", CPType.Unary); - String2CPInstructionType.put("isna", CPType.Unary); - String2CPInstructionType.put("isnan", CPType.Unary); - String2CPInstructionType.put("isinf", CPType.Unary); - String2CPInstructionType.put("printf", CPType.BuiltinNary); - String2CPInstructionType.put("cbind", CPType.BuiltinNary); - String2CPInstructionType.put("rbind", CPType.BuiltinNary); - String2CPInstructionType.put("eval", CPType.BuiltinNary); - String2CPInstructionType.put("list", CPType.BuiltinNary); - + String2CPInstructionType.put( "ba+*" , CPType.AggregateBinary); + String2CPInstructionType.put( "tak+*" , CPType.AggregateTernary); + String2CPInstructionType.put( "tack+*" , CPType.AggregateTernary); + + String2CPInstructionType.put( "uak+" , CPType.AggregateUnary); + String2CPInstructionType.put( "uark+" , CPType.AggregateUnary); + String2CPInstructionType.put( "uack+" , CPType.AggregateUnary); + String2CPInstructionType.put( "uasqk+" , CPType.AggregateUnary); + String2CPInstructionType.put( "uarsqk+" , CPType.AggregateUnary); + String2CPInstructionType.put( "uacsqk+" , CPType.AggregateUnary); + String2CPInstructionType.put( "uamean" , CPType.AggregateUnary); + String2CPInstructionType.put( "uarmean" , CPType.AggregateUnary); + String2CPInstructionType.put( "uacmean" , CPType.AggregateUnary); + String2CPInstructionType.put( "uavar" , CPType.AggregateUnary); + String2CPInstructionType.put( "uarvar" , CPType.AggregateUnary); + String2CPInstructionType.put( "uacvar" , CPType.AggregateUnary); + String2CPInstructionType.put( "uamax" , CPType.AggregateUnary); + String2CPInstructionType.put( "uarmax" , CPType.AggregateUnary); + String2CPInstructionType.put( "uarimax" , CPType.AggregateUnary); + String2CPInstructionType.put( "uacmax" , CPType.AggregateUnary); + String2CPInstructionType.put( "uamin" , CPType.AggregateUnary); + String2CPInstructionType.put( "uarmin" , CPType.AggregateUnary); + String2CPInstructionType.put( "uarimin" , CPType.AggregateUnary); + String2CPInstructionType.put( "uacmin" , CPType.AggregateUnary); + String2CPInstructionType.put( "ua+" , CPType.AggregateUnary); + String2CPInstructionType.put( "uar+" , CPType.AggregateUnary); + String2CPInstructionType.put( "uac+" , CPType.AggregateUnary); + String2CPInstructionType.put( "ua*" , CPType.AggregateUnary); + String2CPInstructionType.put( "uar*" , CPType.AggregateUnary); + String2CPInstructionType.put( "uac*" , CPType.AggregateUnary); + String2CPInstructionType.put( "uatrace" , CPType.AggregateUnary); + String2CPInstructionType.put( "uaktrace", CPType.AggregateUnary); + String2CPInstructionType.put( "nrow" , CPType.AggregateUnary); + String2CPInstructionType.put( "ncol" , CPType.AggregateUnary); + String2CPInstructionType.put( "length" , CPType.AggregateUnary); + String2CPInstructionType.put( "exists" , CPType.AggregateUnary); + String2CPInstructionType.put( "lineage" , CPType.AggregateUnary); + String2CPInstructionType.put( "uacd" , CPType.AggregateUnary); + String2CPInstructionType.put( "uacdr" , CPType.AggregateUnary); + String2CPInstructionType.put( "uacdc" , CPType.AggregateUnary); + String2CPInstructionType.put( "uacdap" , CPType.AggregateUnary); + String2CPInstructionType.put( "uacdapr" , CPType.AggregateUnary); + String2CPInstructionType.put( "uacdapc" , CPType.AggregateUnary); + String2CPInstructionType.put( "unique" , CPType.AggregateUnary); + String2CPInstructionType.put( "uniquer" , CPType.AggregateUnary); + String2CPInstructionType.put( "uniquec" , CPType.AggregateUnary); + + String2CPInstructionType.put( "uaggouterchain", CPType.UaggOuterChain); + + // Arithmetic Instruction Opcodes + String2CPInstructionType.put( "+" , CPType.Binary); + String2CPInstructionType.put( "-" , CPType.Binary); + String2CPInstructionType.put( "*" , CPType.Binary); + String2CPInstructionType.put( "/" , CPType.Binary); + String2CPInstructionType.put( "%%" , CPType.Binary); + String2CPInstructionType.put( "%/%" , CPType.Binary); + String2CPInstructionType.put( "^" , CPType.Binary); + String2CPInstructionType.put( "1-*" , CPType.Binary); //special * case + String2CPInstructionType.put( "^2" , CPType.Binary); //special ^ case + String2CPInstructionType.put( "*2" , CPType.Binary); //special * case + String2CPInstructionType.put( "-nz" , CPType.Binary); //special - case + + // Boolean Instruction Opcodes + String2CPInstructionType.put( "&&" , CPType.Binary); + String2CPInstructionType.put( "||" , CPType.Binary); + String2CPInstructionType.put( "xor" , CPType.Binary); + String2CPInstructionType.put( "bitwAnd", CPType.Binary); + String2CPInstructionType.put( "bitwOr", CPType.Binary); + String2CPInstructionType.put( "bitwXor", CPType.Binary); + String2CPInstructionType.put( "bitwShiftL", CPType.Binary); + String2CPInstructionType.put( "bitwShiftR", CPType.Binary); + String2CPInstructionType.put( "!" , CPType.Unary); + + // Relational Instruction Opcodes + String2CPInstructionType.put( "==" , CPType.Binary); + String2CPInstructionType.put( "!=" , CPType.Binary); + String2CPInstructionType.put( "<" , CPType.Binary); + String2CPInstructionType.put( ">" , CPType.Binary); + String2CPInstructionType.put( "<=" , CPType.Binary); + String2CPInstructionType.put( ">=" , CPType.Binary); + + // Builtin Instruction Opcodes + String2CPInstructionType.put( "log" , CPType.Builtin); + String2CPInstructionType.put( "log_nz" , CPType.Builtin); + + String2CPInstructionType.put( "solve" , CPType.Binary); + String2CPInstructionType.put( "max" , CPType.Binary); + String2CPInstructionType.put( "min" , CPType.Binary); + String2CPInstructionType.put( "dropInvalidType" , CPType.Binary); + String2CPInstructionType.put( "dropInvalidLength" , CPType.Binary); + String2CPInstructionType.put( "freplicate" , CPType.Binary); + String2CPInstructionType.put( "valueSwap" , CPType.Binary); + String2CPInstructionType.put( "applySchema" , CPType.Binary); + String2CPInstructionType.put( "_map" , CPType.Ternary); // _map represents the operation map + + String2CPInstructionType.put( "nmax", CPType.BuiltinNary); + String2CPInstructionType.put( "nmin", CPType.BuiltinNary); + String2CPInstructionType.put( "n+" , CPType.BuiltinNary); + + String2CPInstructionType.put( "exp" , CPType.Unary); + String2CPInstructionType.put( "abs" , CPType.Unary); + String2CPInstructionType.put( "sin" , CPType.Unary); + String2CPInstructionType.put( "cos" , CPType.Unary); + String2CPInstructionType.put( "tan" , CPType.Unary); + String2CPInstructionType.put( "sinh" , CPType.Unary); + String2CPInstructionType.put( "cosh" , CPType.Unary); + String2CPInstructionType.put( "tanh" , CPType.Unary); + String2CPInstructionType.put( "asin" , CPType.Unary); + String2CPInstructionType.put( "acos" , CPType.Unary); + String2CPInstructionType.put( "atan" , CPType.Unary); + String2CPInstructionType.put( "sign" , CPType.Unary); + String2CPInstructionType.put( "sqrt" , CPType.Unary); + String2CPInstructionType.put( "plogp" , CPType.Unary); + String2CPInstructionType.put( "print" , CPType.Unary); + String2CPInstructionType.put( "assert" , CPType.Unary); + String2CPInstructionType.put( "round" , CPType.Unary); + String2CPInstructionType.put( "ceil" , CPType.Unary); + String2CPInstructionType.put( "floor" , CPType.Unary); + String2CPInstructionType.put( "ucumk+", CPType.Unary); + String2CPInstructionType.put( "ucum*" , CPType.Unary); + String2CPInstructionType.put( "ucumk+*" , CPType.Unary); + String2CPInstructionType.put( "ucummin", CPType.Unary); + String2CPInstructionType.put( "ucummax", CPType.Unary); + String2CPInstructionType.put( "stop" , CPType.Unary); + String2CPInstructionType.put( "inverse", CPType.Unary); + String2CPInstructionType.put( "cholesky",CPType.Unary); + String2CPInstructionType.put( "sprop", CPType.Unary); + String2CPInstructionType.put( "sigmoid", CPType.Unary); + String2CPInstructionType.put( "typeOf", CPType.Unary); + String2CPInstructionType.put( "detectSchema", CPType.Unary); + String2CPInstructionType.put( "colnames", CPType.Unary); + String2CPInstructionType.put( "isna", CPType.Unary); + String2CPInstructionType.put( "isnan", CPType.Unary); + String2CPInstructionType.put( "isinf", CPType.Unary); + String2CPInstructionType.put( "printf", CPType.BuiltinNary); + String2CPInstructionType.put( "cbind", CPType.BuiltinNary); + String2CPInstructionType.put( "rbind", CPType.BuiltinNary); + String2CPInstructionType.put( "eval", CPType.BuiltinNary); + String2CPInstructionType.put( "list", CPType.BuiltinNary); + // Parameterized Builtin Functions - String2CPInstructionType.put("autoDiff", CPType.ParameterizedBuiltin); - String2CPInstructionType.put("contains", CPType.ParameterizedBuiltin); - String2CPInstructionType.put("paramserv", CPType.ParameterizedBuiltin); - String2CPInstructionType.put("nvlist", CPType.ParameterizedBuiltin); - String2CPInstructionType.put("cdf", CPType.ParameterizedBuiltin); - String2CPInstructionType.put("invcdf", CPType.ParameterizedBuiltin); - String2CPInstructionType.put("groupedagg", CPType.ParameterizedBuiltin); - String2CPInstructionType.put("rmempty", CPType.ParameterizedBuiltin); - String2CPInstructionType.put("replace", CPType.ParameterizedBuiltin); - String2CPInstructionType.put("lowertri", CPType.ParameterizedBuiltin); - String2CPInstructionType.put("uppertri", CPType.ParameterizedBuiltin); - String2CPInstructionType.put("rexpand", CPType.ParameterizedBuiltin); - String2CPInstructionType.put("toString", CPType.ParameterizedBuiltin); - String2CPInstructionType.put("tokenize", CPType.ParameterizedBuiltin); - String2CPInstructionType.put("transformapply", CPType.ParameterizedBuiltin); - String2CPInstructionType.put("transformdecode", CPType.ParameterizedBuiltin); - String2CPInstructionType.put("transformcolmap", CPType.ParameterizedBuiltin); - String2CPInstructionType.put("transformmeta", CPType.ParameterizedBuiltin); - String2CPInstructionType.put("transformencode", CPType.MultiReturnParameterizedBuiltin); - + String2CPInstructionType.put( "autoDiff" , CPType.ParameterizedBuiltin); + String2CPInstructionType.put( "contains", CPType.ParameterizedBuiltin); + String2CPInstructionType.put("paramserv", CPType.ParameterizedBuiltin); + String2CPInstructionType.put( "nvlist", CPType.ParameterizedBuiltin); + String2CPInstructionType.put( "cdf", CPType.ParameterizedBuiltin); + String2CPInstructionType.put( "invcdf", CPType.ParameterizedBuiltin); + String2CPInstructionType.put( "groupedagg", CPType.ParameterizedBuiltin); + String2CPInstructionType.put( "rmempty" , CPType.ParameterizedBuiltin); + String2CPInstructionType.put( "replace", CPType.ParameterizedBuiltin); + String2CPInstructionType.put( "lowertri", CPType.ParameterizedBuiltin); + String2CPInstructionType.put( "uppertri", CPType.ParameterizedBuiltin); + String2CPInstructionType.put( "rexpand", CPType.ParameterizedBuiltin); + String2CPInstructionType.put( "toString", CPType.ParameterizedBuiltin); + String2CPInstructionType.put( "tokenize", CPType.ParameterizedBuiltin); + String2CPInstructionType.put( "transformapply", CPType.ParameterizedBuiltin); + String2CPInstructionType.put( "transformdecode",CPType.ParameterizedBuiltin); + String2CPInstructionType.put( "transformcolmap",CPType.ParameterizedBuiltin); + String2CPInstructionType.put( "transformmeta", CPType.ParameterizedBuiltin); + String2CPInstructionType.put( "transformencode",CPType.MultiReturnParameterizedBuiltin); + // Ternary Instruction Opcodes - String2CPInstructionType.put("+*", CPType.Ternary); - String2CPInstructionType.put("-*", CPType.Ternary); - String2CPInstructionType.put("ifelse", CPType.Ternary); - - // Variable Instruction Opcodes - String2CPInstructionType.put("assignvar", CPType.Variable); - String2CPInstructionType.put("cpvar", CPType.Variable); - String2CPInstructionType.put("mvvar", CPType.Variable); - String2CPInstructionType.put("rmvar", CPType.Variable); - String2CPInstructionType.put("rmfilevar", CPType.Variable); - String2CPInstructionType.put(OpOp1.CAST_AS_SCALAR.toString(), CPType.Variable); - String2CPInstructionType.put(OpOp1.CAST_AS_MATRIX.toString(), CPType.Variable); - String2CPInstructionType.put("cast_as_frame", CPType.Variable); - String2CPInstructionType.put(OpOp1.CAST_AS_FRAME.toString(), CPType.Variable); - String2CPInstructionType.put(OpOp1.CAST_AS_LIST.toString(), CPType.Variable); - String2CPInstructionType.put(OpOp1.CAST_AS_DOUBLE.toString(), CPType.Variable); - String2CPInstructionType.put(OpOp1.CAST_AS_INT.toString(), CPType.Variable); - String2CPInstructionType.put(OpOp1.CAST_AS_BOOLEAN.toString(), CPType.Variable); - String2CPInstructionType.put("attachfiletovar", CPType.Variable); - String2CPInstructionType.put("read", CPType.Variable); - String2CPInstructionType.put("write", CPType.Variable); - String2CPInstructionType.put("createvar", CPType.Variable); + String2CPInstructionType.put( "+*", CPType.Ternary); + String2CPInstructionType.put( "-*", CPType.Ternary); + String2CPInstructionType.put( "ifelse", CPType.Ternary); + + // Variable Instruction Opcodes + String2CPInstructionType.put( "assignvar" , CPType.Variable); + String2CPInstructionType.put( "cpvar" , CPType.Variable); + String2CPInstructionType.put( "mvvar" , CPType.Variable); + String2CPInstructionType.put( "rmvar" , CPType.Variable); + String2CPInstructionType.put( "rmfilevar" , CPType.Variable); + String2CPInstructionType.put( OpOp1.CAST_AS_SCALAR.toString(), CPType.Variable); + String2CPInstructionType.put( OpOp1.CAST_AS_MATRIX.toString(), CPType.Variable); + String2CPInstructionType.put( "cast_as_frame", CPType.Variable); + String2CPInstructionType.put( OpOp1.CAST_AS_FRAME.toString(), CPType.Variable); + String2CPInstructionType.put( OpOp1.CAST_AS_LIST.toString(), CPType.Variable); + String2CPInstructionType.put( OpOp1.CAST_AS_DOUBLE.toString(), CPType.Variable); + String2CPInstructionType.put( OpOp1.CAST_AS_INT.toString(), CPType.Variable); + String2CPInstructionType.put( OpOp1.CAST_AS_BOOLEAN.toString(), CPType.Variable); + String2CPInstructionType.put( "attachfiletovar" , CPType.Variable); + String2CPInstructionType.put( "read" , CPType.Variable); + String2CPInstructionType.put( "write" , CPType.Variable); + String2CPInstructionType.put( "createvar" , CPType.Variable); // Reorg Instruction Opcodes (repositioning of existing values) - String2CPInstructionType.put("r'", CPType.Reorg); - String2CPInstructionType.put("rev", CPType.Reorg); - String2CPInstructionType.put("rdiag", CPType.Reorg); - String2CPInstructionType.put("rshape", CPType.Reshape); - String2CPInstructionType.put("rsort", CPType.Reorg); + String2CPInstructionType.put( "r'" , CPType.Reorg); + String2CPInstructionType.put( "rev" , CPType.Reorg); + String2CPInstructionType.put( "rdiag" , CPType.Reorg); + String2CPInstructionType.put( "rshape" , CPType.Reshape); + String2CPInstructionType.put( "rsort" , CPType.Reorg); // Opcodes related to convolutions - String2CPInstructionType.put("relu_backward", CPType.Dnn); - String2CPInstructionType.put("relu_maxpooling", CPType.Dnn); - String2CPInstructionType.put("relu_maxpooling_backward", CPType.Dnn); - String2CPInstructionType.put("maxpooling", CPType.Dnn); - String2CPInstructionType.put("maxpooling_backward", CPType.Dnn); - String2CPInstructionType.put("avgpooling", CPType.Dnn); - String2CPInstructionType.put("avgpooling_backward", CPType.Dnn); - String2CPInstructionType.put("conv2d", CPType.Dnn); - String2CPInstructionType.put("conv2d_bias_add", CPType.Dnn); - String2CPInstructionType.put("conv2d_backward_filter", CPType.Dnn); - String2CPInstructionType.put("conv2d_backward_data", CPType.Dnn); - String2CPInstructionType.put("bias_add", CPType.Dnn); - String2CPInstructionType.put("bias_multiply", CPType.Dnn); - String2CPInstructionType.put("batch_norm2d", CPType.Dnn); - String2CPInstructionType.put("batch_norm2d_backward", CPType.Dnn); - + String2CPInstructionType.put( "relu_backward" , CPType.Dnn); + String2CPInstructionType.put( "relu_maxpooling" , CPType.Dnn); + String2CPInstructionType.put( "relu_maxpooling_backward" , CPType.Dnn); + String2CPInstructionType.put( "maxpooling" , CPType.Dnn); + String2CPInstructionType.put( "maxpooling_backward" , CPType.Dnn); + String2CPInstructionType.put( "avgpooling" , CPType.Dnn); + String2CPInstructionType.put( "avgpooling_backward" , CPType.Dnn); + String2CPInstructionType.put( "conv2d" , CPType.Dnn); + String2CPInstructionType.put( "conv2d_bias_add" , CPType.Dnn); + String2CPInstructionType.put( "conv2d_backward_filter" , CPType.Dnn); + String2CPInstructionType.put( "conv2d_backward_data" , CPType.Dnn); + String2CPInstructionType.put( "bias_add" , CPType.Dnn); + String2CPInstructionType.put( "bias_multiply" , CPType.Dnn); + String2CPInstructionType.put( "batch_norm2d", CPType.Dnn); + String2CPInstructionType.put( "batch_norm2d_backward", CPType.Dnn); + // Quaternary instruction opcodes - String2CPInstructionType.put("wsloss", CPType.Quaternary); - String2CPInstructionType.put("wsigmoid", CPType.Quaternary); - String2CPInstructionType.put("wdivmm", CPType.Quaternary); - String2CPInstructionType.put("wcemm", CPType.Quaternary); - String2CPInstructionType.put("wumm", CPType.Quaternary); - + String2CPInstructionType.put( "wsloss" , CPType.Quaternary); + String2CPInstructionType.put( "wsigmoid", CPType.Quaternary); + String2CPInstructionType.put( "wdivmm", CPType.Quaternary); + String2CPInstructionType.put( "wcemm", CPType.Quaternary); + String2CPInstructionType.put( "wumm", CPType.Quaternary); + // User-defined function Opcodes String2CPInstructionType.put(FunctionOp.OPCODE, CPType.FCall); String2CPInstructionType.put(Append.OPCODE, CPType.Append); - String2CPInstructionType.put("remove", CPType.Append); - + String2CPInstructionType.put( "remove", CPType.Append); + // data generation opcodes String2CPInstructionType.put( DataGen.RAND_OPCODE , CPType.Rand); String2CPInstructionType.put( DataGen.SEQ_OPCODE , CPType.Rand); @@ -332,7 +332,7 @@ public class CPInstructionParser extends InstructionParser { String2CPInstructionType.put( "qr", CPType.MultiReturnBuiltin); String2CPInstructionType.put( "lu", CPType.MultiReturnBuiltin); String2CPInstructionType.put( "eigen", CPType.MultiReturnBuiltin); - String2CPInstructionType.put("fft", CPType.MultiReturnBuiltin); + String2CPInstructionType.put("fft", CPType.MultiReturnBuiltin); String2CPInstructionType.put("ifft", CPType.MultiReturnComplexMatrixBuiltin); String2CPInstructionType.put( "svd", CPType.MultiReturnBuiltin); @@ -349,147 +349,146 @@ public class CPInstructionParser extends InstructionParser { String2CPInstructionType.put( "sql", CPType.Sql); } - public static CPInstruction parseSingleInstruction(String str) { - if (str == null || str.isEmpty()) + public static CPInstruction parseSingleInstruction (String str ) { + if ( str == null || str.isEmpty() ) return null; - CPType cptype = InstructionUtils.getCPType(str); - if (cptype == null) + CPType cptype = InstructionUtils.getCPType(str); + if ( cptype == null ) throw new DMLRuntimeException("Unable derive cptype for instruction: " + str); CPInstruction cpinst = parseSingleInstruction(cptype, str); - if (cpinst == null) + if ( cpinst == null ) throw new DMLRuntimeException("Unable to parse instruction: " + str); return cpinst; } - - public static CPInstruction parseSingleInstruction(CPType cptype, String str) { + + public static CPInstruction parseSingleInstruction ( CPType cptype, String str ) { ExecType execType; - if (str == null || str.isEmpty()) + if ( str == null || str.isEmpty() ) return null; - switch (cptype) { + switch(cptype) { case AggregateUnary: return AggregateUnaryCPInstruction.parseInstruction(str); - + case AggregateBinary: return AggregateBinaryCPInstruction.parseInstruction(str); - + case AggregateTernary: return AggregateTernaryCPInstruction.parseInstruction(str); - + case Unary: return UnaryCPInstruction.parseInstruction(str); case Binary: return BinaryCPInstruction.parseInstruction(str); - + case Ternary: return TernaryCPInstruction.parseInstruction(str); - + case Quaternary: return QuaternaryCPInstruction.parseInstruction(str); - + case BuiltinNary: return BuiltinNaryCPInstruction.parseInstruction(str); - + case Ctable: return CtableCPInstruction.parseInstruction(str); - + case Reorg: return ReorgCPInstruction.parseInstruction(str); - + case Dnn: return DnnCPInstruction.parseInstruction(str); - + case UaggOuterChain: return UaggOuterChainCPInstruction.parseInstruction(str); - + case Reshape: return ReshapeCPInstruction.parseInstruction(str); - + case Append: return AppendCPInstruction.parseInstruction(str); - + case Variable: return VariableCPInstruction.parseInstruction(str); - + case Rand: return DataGenCPInstruction.parseInstruction(str); case StringInit: return StringInitCPInstruction.parseInstruction(str); - + case FCall: return FunctionCallCPInstruction.parseInstruction(str); case ParameterizedBuiltin: return ParameterizedBuiltinCPInstruction.parseInstruction(str); - + case MultiReturnParameterizedBuiltin: return MultiReturnParameterizedBuiltinCPInstruction.parseInstruction(str); - + case MultiReturnComplexMatrixBuiltin: return MultiReturnComplexMatrixBuiltinCPInstruction.parseInstruction(str); - + case MultiReturnBuiltin: return MultiReturnBuiltinCPInstruction.parseInstruction(str); - + case QSort: return QuantileSortCPInstruction.parseInstruction(str); - + case QPick: return QuantilePickCPInstruction.parseInstruction(str); - + case MatrixIndexing: - execType = ExecType.valueOf(str.split(Instruction.OPERAND_DELIM)[0]); - if (execType == ExecType.CP) + execType = ExecType.valueOf( str.split(Instruction.OPERAND_DELIM)[0] ); + if( execType == ExecType.CP ) return IndexingCPInstruction.parseInstruction(str); - else // exectype CP_FILE + else //exectype CP_FILE return MatrixIndexingCPFileInstruction.parseInstruction(str); - - case Builtin: + + case Builtin: String[] parts = InstructionUtils.getInstructionPartsWithValueType(str); - if (parts[0].equals("log") || parts[0].equals("log_nz")) { - if (InstructionUtils.isInteger(parts[3])) // B=log(A), y=log(x) - // We exploit the fact the number of threads is specified as an integer at parts - // 3. + if(parts[0].equals("log") || parts[0].equals("log_nz")) { + if(InstructionUtils.isInteger(parts[3])) // B=log(A), y=log(x) + // We exploit the fact the number of threads is specified as an integer at parts 3. return UnaryCPInstruction.parseInstruction(str); else // B=log(A,10), y=log(x,10) return BinaryCPInstruction.parseInstruction(str); } - throw new DMLRuntimeException("Invalid Builtin Instruction: " + str); - + throw new DMLRuntimeException("Invalid Builtin Instruction: " + str ); + case MMTSJ: return MMTSJCPInstruction.parseInstruction(str); - + case PMMJ: return PMMJCPInstruction.parseInstruction(str); - + case MMChain: return MMChainCPInstruction.parseInstruction(str); - + case CentralMoment: return CentralMomentCPInstruction.parseInstruction(str); - + case Covariance: return CovarianceCPInstruction.parseInstruction(str); case Compression: return CompressionCPInstruction.parseInstruction(str); - + case DeCompression: return DeCompressionCPInstruction.parseInstruction(str); - + case Local: return LocalCPInstruction.parseInstruction(str); case SpoofFused: return SpoofCPInstruction.parseInstruction(str); - + case Sql: return SqlCPInstruction.parseInstruction(str); - + case Prefetch: return PrefetchCPInstruction.parseInstruction(str); - + case Broadcast: return BroadcastCPInstruction.parseInstruction(str); @@ -497,7 +496,7 @@ public static CPInstruction parseSingleInstruction(CPType cptype, String str) { return EvictCPInstruction.parseInstruction(str); default: - throw new DMLRuntimeException("Invalid CP Instruction Type: " + cptype); + throw new DMLRuntimeException("Invalid CP Instruction Type: " + cptype ); } } } From dbf01dd43290ebfab8b9a8d4b909f3554c6d8ac8 Mon Sep 17 00:00:00 2001 From: Frederic Zoepffel Date: Fri, 9 Feb 2024 00:02:10 +0100 Subject: [PATCH 056/133] reverse formatting CPInstruction --- .../instructions/cp/CPInstruction.java | 88 +++++++++---------- 1 file changed, 42 insertions(+), 46 deletions(-) diff --git a/src/main/java/org/apache/sysds/runtime/instructions/cp/CPInstruction.java b/src/main/java/org/apache/sysds/runtime/instructions/cp/CPInstruction.java index 23627060d04..aaf8227e25d 100644 --- a/src/main/java/org/apache/sysds/runtime/instructions/cp/CPInstruction.java +++ b/src/main/java/org/apache/sysds/runtime/instructions/cp/CPInstruction.java @@ -19,6 +19,7 @@ package org.apache.sysds.runtime.instructions.cp; + import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.sysds.api.DMLScript; @@ -66,7 +67,7 @@ protected CPInstruction(CPType type, Operator op, String opcode, String istr) { instOpcode = opcode; _requiresLabelUpdate = super.requiresLabelUpdate(); } - + @Override public IType getType() { return IType.CONTROL_PROGRAM; @@ -75,7 +76,7 @@ public IType getType() { public CPType getCPInstructionType() { return _cptype; } - + @Override public boolean requiresLabelUpdate() { return _requiresLabelUpdate; @@ -88,32 +89,31 @@ public String getGraphString() { @Override public Instruction preprocessInstruction(ExecutionContext ec) { - // default preprocess behavior (e.g., debug state, lineage) + //default preprocess behavior (e.g., debug state, lineage) Instruction tmp = super.preprocessInstruction(ec); - // instruction patching - if (tmp.requiresLabelUpdate()) { // update labels only if required - // note: no exchange of updated instruction as labels might change in the - // general case + //instruction patching + if( tmp.requiresLabelUpdate() ) { //update labels only if required + //note: no exchange of updated instruction as labels might change in the general case String updInst = updateLabels(tmp.toString(), ec.getVariables()); tmp = CPInstructionParser.parseSingleInstruction(updInst); // Corrected lineage trace for patched instructions if (DMLScript.LINEAGE) ec.traceLineage(tmp); } - - // robustness federated instructions (runtime assignment) - if (ConfigurationManager.isFederatedRuntimePlanner()) { + + //robustness federated instructions (runtime assignment) + if( ConfigurationManager.isFederatedRuntimePlanner() ) { tmp = FEDInstructionUtils.checkAndReplaceCP(tmp, ec); - // NOTE: Retracing of lineage is not needed as the lineage trace - // is same for an instruction and its FED version. + //NOTE: Retracing of lineage is not needed as the lineage trace + //is same for an instruction and its FED version. } - + tmp = PrivacyPropagator.preprocessInstruction(tmp, ec); return tmp; } - @Override + @Override public abstract void processInstruction(ExecutionContext ec); @Override @@ -121,63 +121,60 @@ public void postprocessInstruction(ExecutionContext ec) { if (DMLScript.LINEAGE_DEBUGGER) ec.maintainLineageDebuggerInfo(this); } - + /** - * Takes a delimited string of instructions, and replaces ALL placeholder labels + * Takes a delimited string of instructions, and replaces ALL placeholder labels * (such as ##mVar2## and ##Var5##) in ALL instructions. - * - * @param instList instruction list as string + * + * @param instList instruction list as string * @param labelValueMapping local variable map * @return instruction list after replacement */ - public static String updateLabels(String instList, LocalVariableMap labelValueMapping) { + public static String updateLabels (String instList, LocalVariableMap labelValueMapping) { - if (!instList.contains(Lop.VARIABLE_NAME_PLACEHOLDER)) + if ( !instList.contains(Lop.VARIABLE_NAME_PLACEHOLDER) ) return instList; - + StringBuilder updateInstList = new StringBuilder(); - String[] ilist = instList.split(Lop.INSTRUCTION_DELIMITOR); - - for (int i = 0; i < ilist.length; i++) { - if (i > 0) + String[] ilist = instList.split(Lop.INSTRUCTION_DELIMITOR); + + for ( int i=0; i < ilist.length; i++ ) { + if ( i > 0 ) updateInstList.append(Lop.INSTRUCTION_DELIMITOR); - - updateInstList.append(updateInstLabels(ilist[i], labelValueMapping)); + + updateInstList.append( updateInstLabels(ilist[i], labelValueMapping)); } return updateInstList.toString(); } - /** - * Replaces ALL placeholder strings (such as ##mVar2## and ##Var5##) in a single - * instruction. - * + /** + * Replaces ALL placeholder strings (such as ##mVar2## and ##Var5##) in a single instruction. + * * @param inst string instruction - * @param map local variable map + * @param map local variable map * @return string instruction after replacement */ private static String updateInstLabels(String inst, LocalVariableMap map) { - if (inst.contains(Lop.VARIABLE_NAME_PLACEHOLDER)) { + if ( inst.contains(Lop.VARIABLE_NAME_PLACEHOLDER) ) { int skip = Lop.VARIABLE_NAME_PLACEHOLDER.length(); - while (inst.contains(Lop.VARIABLE_NAME_PLACEHOLDER)) { - int startLoc = inst.indexOf(Lop.VARIABLE_NAME_PLACEHOLDER) + skip; + while ( inst.contains(Lop.VARIABLE_NAME_PLACEHOLDER) ) { + int startLoc = inst.indexOf(Lop.VARIABLE_NAME_PLACEHOLDER)+skip; String varName = inst.substring(startLoc, inst.indexOf(Lop.VARIABLE_NAME_PLACEHOLDER, startLoc)); String replacement = getVarNameReplacement(inst, varName, map); - inst = inst.replaceAll(Lop.VARIABLE_NAME_PLACEHOLDER + varName + Lop.VARIABLE_NAME_PLACEHOLDER, - replacement); + inst = inst.replaceAll(Lop.VARIABLE_NAME_PLACEHOLDER + varName + Lop.VARIABLE_NAME_PLACEHOLDER, replacement); } } return inst; } - + /** - * Computes the replacement string for a given variable name placeholder string - * (e.g., ##mVar2## or ##Var5##). The replacement is a HDFS filename for matrix - * variables, and is the actual value (stored in symbol table) for scalar - * variables. + * Computes the replacement string for a given variable name placeholder string + * (e.g., ##mVar2## or ##Var5##). The replacement is a HDFS filename for matrix + * variables, and is the actual value (stored in symbol table) for scalar variables. * - * @param inst instruction + * @param inst instruction * @param varName variable name - * @param map local variable map + * @param map local variable map * @return string variable name */ private static String getVarNameReplacement(String inst, String varName, LocalVariableMap map) { @@ -192,8 +189,7 @@ private static String getVarNameReplacement(String inst, String varName, LocalVa replacement = "" + ((ScalarObject) val).getStringValue(); return replacement; } else { - throw new DMLRuntimeException( - "Variable (" + varName + ") in Instruction (" + inst + ") is not found in the variablemap."); + throw new DMLRuntimeException("Variable (" + varName + ") in Instruction (" + inst + ") is not found in the variablemap."); } } } From 1120e7810796177aba7714e2753bbadb26fefa95 Mon Sep 17 00:00:00 2001 From: Frederic Zoepffel Date: Fri, 9 Feb 2024 00:08:07 +0100 Subject: [PATCH 057/133] reversed formatting PrivacyPropagator --- .../propagation/PrivacyPropagator.java | 427 ++++++++---------- 1 file changed, 187 insertions(+), 240 deletions(-) diff --git a/src/main/java/org/apache/sysds/runtime/privacy/propagation/PrivacyPropagator.java b/src/main/java/org/apache/sysds/runtime/privacy/propagation/PrivacyPropagator.java index 4d2996b7856..13320f37511 100644 --- a/src/main/java/org/apache/sysds/runtime/privacy/propagation/PrivacyPropagator.java +++ b/src/main/java/org/apache/sysds/runtime/privacy/propagation/PrivacyPropagator.java @@ -70,23 +70,24 @@ import org.apache.wink.json4j.JSONObject; /** - * Class with static methods merging privacy constraints of operands - * in expressions to generate the privacy constraints of the output. + * Class with static methods merging privacy constraints of operands + * in expressions to generate the privacy constraints of the output. */ -public class PrivacyPropagator { +public class PrivacyPropagator +{ /** * Parses the privacy constraint of the given metadata object * and sets the field of the given Data if the privacy constraint is not null. - * - * @param cd data for which privacy constraint is set + * @param cd data for which privacy constraint is set * @param mtd metadata object * @return data object with privacy constraint set * @throws JSONException during parsing of metadata */ public static Data parseAndSetPrivacyConstraint(Data cd, JSONObject mtd) - throws JSONException { + throws JSONException + { PrivacyConstraint mtdPrivConstraint = parseAndReturnPrivacyConstraint(mtd); - if (mtdPrivConstraint != null) + if ( mtdPrivConstraint != null ) cd.setPrivacyConstraints(mtdPrivConstraint); return cd; } @@ -94,102 +95,94 @@ public static Data parseAndSetPrivacyConstraint(Data cd, JSONObject mtd) /** * Parses the privacy constraint of the given metadata object * or returns null if no privacy constraint is set in the metadata. - * * @param mtd metadata * @return privacy constraint parsed from metadata object * @throws JSONException during parsing of metadata */ public static PrivacyConstraint parseAndReturnPrivacyConstraint(JSONObject mtd) - throws JSONException { - if (mtd.containsKey(DataExpression.PRIVACY)) { + throws JSONException + { + if ( mtd.containsKey(DataExpression.PRIVACY) ) { String privacyLevel = mtd.getString(DataExpression.PRIVACY); - if (privacyLevel != null) + if ( privacyLevel != null ) return new PrivacyConstraint(PrivacyLevel.valueOf(privacyLevel)); } return null; } - private static boolean anyInputHasLevel(PrivacyLevel[] inputLevels, PrivacyLevel targetLevel) { + private static boolean anyInputHasLevel(PrivacyLevel[] inputLevels, PrivacyLevel targetLevel){ return Arrays.stream(inputLevels).anyMatch(i -> i == targetLevel); } /** - * Returns the output privacy level based on the given input privacy levels and - * operator type. + * Returns the output privacy level based on the given input privacy levels and operator type. * It represents the basic logic of privacy propagation: * * Unary input: - * Input | NonAggregate | Aggregate + * Input | NonAggregate | Aggregate * ----------------------------------- - * priv | priv | priv - * privAgg | privAgg | none - * none | none | none + * priv | priv | priv + * privAgg | privAgg | none + * none | none | none * * Binary input: - * Input | NonAggregate | Aggregate + * Input | NonAggregate | Aggregate * -------------------------------------------- - * priv-priv | priv | priv - * priv-privAgg | priv | priv - * priv-none | priv | priv - * privAgg-priv | priv | priv - * none-priv | priv | priv - * privAgg-privAgg | privAgg | none - * none-none | none | none - * privAgg-none | privAgg | none - * none-privAgg | privAgg | none + * priv-priv | priv | priv + * priv-privAgg | priv | priv + * priv-none | priv | priv + * privAgg-priv | priv | priv + * none-priv | priv | priv + * privAgg-privAgg | privAgg | none + * none-none | none | none + * privAgg-none | privAgg | none + * none-privAgg | privAgg | none * - * @param inputLevels privacy levels of the input - * @param operatorType type of the operator which is either an aggregation - * (Aggregate) or not an aggregation (NonAggregate) + * @param inputLevels privacy levels of the input + * @param operatorType type of the operator which is either an aggregation (Aggregate) or not an aggregation (NonAggregate) * @return output privacy level */ - public static PrivacyLevel corePropagation(PrivacyLevel[] inputLevels, OperatorType operatorType) { + public static PrivacyLevel corePropagation(PrivacyLevel[] inputLevels, OperatorType operatorType){ if (anyInputHasLevel(inputLevels, PrivacyLevel.Private)) return PrivacyLevel.Private; if (operatorType == OperatorType.Aggregate) return PrivacyLevel.None; - if (operatorType == OperatorType.NonAggregate && anyInputHasLevel(inputLevels, PrivacyLevel.PrivateAggregation)) + if (operatorType == OperatorType.NonAggregate && anyInputHasLevel(inputLevels,PrivacyLevel.PrivateAggregation)) return PrivacyLevel.PrivateAggregation; return PrivacyLevel.None; } /** - * Merges the given privacy constraints with the core propagation using the - * given operator type. - * + * Merges the given privacy constraints with the core propagation using the given operator type. * @param privacyConstraints array of privacy constraints to merge - * @param operatorType type of operation to use when merging with the core - * propagation + * @param operatorType type of operation to use when merging with the core propagation * @return merged privacy constraint */ - private static PrivacyConstraint mergeNary(PrivacyConstraint[] privacyConstraints, OperatorType operatorType) { + private static PrivacyConstraint mergeNary(PrivacyConstraint[] privacyConstraints, OperatorType operatorType){ PrivacyLevel[] privacyLevels = Arrays.stream(privacyConstraints) - .map(constraint -> { - if (constraint != null) - return constraint.getPrivacyLevel(); - else - return PrivacyLevel.None; - }) - .toArray(PrivacyLevel[]::new); + .map(constraint -> { + if (constraint != null) + return constraint.getPrivacyLevel(); + else return PrivacyLevel.None; + }) + .toArray(PrivacyLevel[]::new); PrivacyLevel outputPrivacyLevel = corePropagation(privacyLevels, operatorType); return new PrivacyConstraint(outputPrivacyLevel); } /** - * Merges the input privacy constraints using the core propagation with - * NonAggregate operator type. - * + * Merges the input privacy constraints using the core propagation with NonAggregate operator type. * @param privacyConstraint1 first privacy constraint * @param privacyConstraint2 second privacy constraint * @return merged privacy constraint */ - public static PrivacyConstraint mergeBinary(PrivacyConstraint privacyConstraint1, - PrivacyConstraint privacyConstraint2) { - if (privacyConstraint1 != null && privacyConstraint2 != null) { - PrivacyLevel[] privacyLevels = new PrivacyLevel[] { - privacyConstraint1.getPrivacyLevel(), privacyConstraint2.getPrivacyLevel() }; + public static PrivacyConstraint mergeBinary(PrivacyConstraint privacyConstraint1, PrivacyConstraint privacyConstraint2) { + if (privacyConstraint1 != null && privacyConstraint2 != null){ + PrivacyLevel[] privacyLevels = new PrivacyLevel[]{ + privacyConstraint1.getPrivacyLevel(),privacyConstraint2.getPrivacyLevel()}; return new PrivacyConstraint(corePropagation(privacyLevels, OperatorType.NonAggregate)); - } else if (privacyConstraint1 != null) + } + else if (privacyConstraint1 != null) return privacyConstraint1; else if (privacyConstraint2 != null) return privacyConstraint2; @@ -198,43 +191,40 @@ else if (privacyConstraint2 != null) /** * Propagate privacy constraints from input hops to given hop. - * * @param hop which the privacy constraints are propagated to */ - public static void hopPropagation(Hop hop) { + public static void hopPropagation(Hop hop){ hopPropagation(hop, hop.getInput()); } /** * Propagate privacy constraints from input hops to given hop. - * - * @param hop which the privacy constraints are propagated to + * @param hop which the privacy constraints are propagated to * @param inputHops inputs to given hop */ - public static void hopPropagation(Hop hop, ArrayList inputHops) { + public static void hopPropagation(Hop hop, ArrayList inputHops){ PrivacyConstraint[] inputConstraints = inputHops.stream() - .map(Hop::getPrivacy).toArray(PrivacyConstraint[]::new); + .map(Hop::getPrivacy).toArray(PrivacyConstraint[]::new); OperatorType opType = getOpType(hop); hop.setPrivacy(mergeNary(inputConstraints, opType)); if (opType == null && Arrays.stream(inputConstraints).anyMatch(Objects::nonNull)) throw new DMLException("Input has constraint but hop type not recognized by PrivacyPropagator. " + - "Hop is " + hop + " " + hop.getClass()); + "Hop is " + hop + " " + hop.getClass()); } /** * Get operator type of given hop. * Returns null if hop type is not known. - * * @param hop for which operator type is returned * @return operator type of hop or null if hop type is unknown */ - private static OperatorType getOpType(Hop hop) { - if (hop instanceof TernaryOp || hop instanceof BinaryOp || hop instanceof ReorgOp - || hop instanceof DataOp || hop instanceof LiteralOp || hop instanceof NaryOp - || hop instanceof DataGenOp || hop instanceof FunctionOp || hop instanceof IndexingOp - || hop instanceof ParameterizedBuiltinOp || hop instanceof LeftIndexingOp) + private static OperatorType getOpType(Hop hop){ + if ( hop instanceof TernaryOp || hop instanceof BinaryOp || hop instanceof ReorgOp + || hop instanceof DataOp || hop instanceof LiteralOp || hop instanceof NaryOp + || hop instanceof DataGenOp || hop instanceof FunctionOp || hop instanceof IndexingOp + || hop instanceof ParameterizedBuiltinOp || hop instanceof LeftIndexingOp ) return OperatorType.NonAggregate; - else if (hop instanceof AggBinaryOp || hop instanceof AggUnaryOp || hop instanceof UnaryOp) + else if ( hop instanceof AggBinaryOp || hop instanceof AggUnaryOp || hop instanceof UnaryOp ) return OperatorType.Aggregate; else return null; @@ -244,17 +234,16 @@ else if (hop instanceof AggBinaryOp || hop instanceof AggUnaryOp || hop instance * Propagate privacy constraints to output variables * based on privacy constraint of CPOperand output in instruction * which has been set during privacy propagation preprocessing. - * * @param inst instruction for which privacy constraints are propagated - * @param ec execution context + * @param ec execution context */ - public static void postProcessInstruction(Instruction inst, ExecutionContext ec) { + public static void postProcessInstruction(Instruction inst, ExecutionContext ec){ // if inst has output List instOutputs = getOutputOperands(inst); - if (!instOutputs.isEmpty()) { - for (CPOperand output : instOutputs) { + if (!instOutputs.isEmpty()){ + for ( CPOperand output : instOutputs ){ PrivacyConstraint outputPrivacyConstraint = output.getPrivacyConstraint(); - if (PrivacyUtils.someConstraintSetUnary(outputPrivacyConstraint)) + if ( PrivacyUtils.someConstraintSetUnary(outputPrivacyConstraint) ) setOutputPrivacyConstraint(ec, outputPrivacyConstraint, output.getName()); } } @@ -263,16 +252,14 @@ public static void postProcessInstruction(Instruction inst, ExecutionContext ec) /** * Propagate privacy constraints from input to output CPOperands * in case the privacy constraints of the input are activated. - * * @param inst instruction for which the privacy constraints are propagated - * @param ec execution context - * @return instruction with propagated privacy constraints (usually the same - * instance as the input inst) + * @param ec execution context + * @return instruction with propagated privacy constraints (usually the same instance as the input inst) */ - public static Instruction preprocessInstruction(Instruction inst, ExecutionContext ec) { - switch (inst.getType()) { + public static Instruction preprocessInstruction(Instruction inst, ExecutionContext ec){ + switch ( inst.getType() ){ case CONTROL_PROGRAM: - return preprocessCPInstruction((CPInstruction) inst, ec); + return preprocessCPInstruction( (CPInstruction) inst, ec ); case BREAKPOINT: case SPARK: case GPU: @@ -283,8 +270,8 @@ public static Instruction preprocessInstruction(Instruction inst, ExecutionConte } } - private static Instruction preprocessCPInstruction(CPInstruction inst, ExecutionContext ec) { - switch (inst.getCPInstructionType()) { + private static Instruction preprocessCPInstruction(CPInstruction inst, ExecutionContext ec){ + switch(inst.getCPInstructionType()){ case Binary: case Builtin: case BuiltinNary: @@ -298,17 +285,16 @@ private static Instruction preprocessCPInstruction(CPInstruction inst, Execution case MultiReturnComplexMatrixBuiltin: case MultiReturnParameterizedBuiltin: case MatrixIndexing: - return mergePrivacyConstraintsFromInput(inst, ec, OperatorType.NonAggregate); + return mergePrivacyConstraintsFromInput( inst, ec, OperatorType.NonAggregate ); case AggregateTernary: case AggregateUnary: return mergePrivacyConstraintsFromInput(inst, ec, OperatorType.Aggregate); case Append: return preprocessAppendCPInstruction((AppendCPInstruction) inst, ec); case AggregateBinary: - if (inst instanceof AggregateBinaryCPInstruction) - return preprocessAggregateBinaryCPInstruction((AggregateBinaryCPInstruction) inst, ec); - else - return throwExceptionIfInputOrInstPrivacy(inst, ec); + if ( inst instanceof AggregateBinaryCPInstruction ) + return preprocessAggregateBinaryCPInstruction((AggregateBinaryCPInstruction)inst, ec); + else return throwExceptionIfInputOrInstPrivacy(inst, ec); case MMTSJ: OperatorType mmtsjOpType = OperatorType.getAggregationType((MMTSJCPInstruction) inst, ec); return mergePrivacyConstraintsFromInput(inst, ec, mmtsjOpType); @@ -322,8 +308,8 @@ private static Instruction preprocessCPInstruction(CPInstruction inst, Execution } } - private static Instruction preprocessVariableCPInstruction(VariableCPInstruction inst, ExecutionContext ec) { - switch (inst.getVariableOpcode()) { + private static Instruction preprocessVariableCPInstruction(VariableCPInstruction inst, ExecutionContext ec){ + switch ( inst.getVariableOpcode() ) { case CopyVariable: case MoveVariable: case RemoveVariableAndFile: @@ -340,10 +326,9 @@ private static Instruction preprocessVariableCPInstruction(VariableCPInstruction return propagateSecondInputPrivacy(inst, ec); case AssignVariable: case RemoveVariable: - return mergePrivacyConstraintsFromInput(inst, ec, OperatorType.NonAggregate); + return mergePrivacyConstraintsFromInput( inst, ec, OperatorType.NonAggregate ); case Read: - // Adds scalar object to variable map, hence input (data type and filename) - // privacy should not be propagated + // Adds scalar object to variable map, hence input (data type and filename) privacy should not be propagated return inst; default: return throwExceptionIfInputOrInstPrivacy(inst, ec); @@ -353,26 +338,23 @@ private static Instruction preprocessVariableCPInstruction(VariableCPInstruction /** * Propagates fine-grained constraints if input has fine-grained constraints, * otherwise it propagates general constraints. - * * @param inst aggregate binary instruction for which constraints are propagated - * @param ec execution context - * @return instruction with merged privacy constraints propagated to it and - * output CPOperand + * @param ec execution context + * @return instruction with merged privacy constraints propagated to it and output CPOperand */ - private static Instruction preprocessAggregateBinaryCPInstruction(AggregateBinaryCPInstruction inst, - ExecutionContext ec) { + private static Instruction preprocessAggregateBinaryCPInstruction(AggregateBinaryCPInstruction inst, ExecutionContext ec){ PrivacyConstraint[] privacyConstraints = getInputPrivacyConstraints(ec, inst.getInputs()); - if (PrivacyUtils.someConstraintSetBinary(privacyConstraints)) { + if ( PrivacyUtils.someConstraintSetBinary(privacyConstraints) ){ PrivacyConstraint mergedPrivacyConstraint; - if ((privacyConstraints[0] != null && privacyConstraints[0].hasFineGrainedConstraints()) || - (privacyConstraints[1] != null && privacyConstraints[1].hasFineGrainedConstraints())) { + if ( (privacyConstraints[0] != null && privacyConstraints[0].hasFineGrainedConstraints() ) || + (privacyConstraints[1] != null && privacyConstraints[1].hasFineGrainedConstraints() )){ MatrixBlock input1 = ec.getMatrixInput(inst.input1.getName()); MatrixBlock input2 = ec.getMatrixInput(inst.input2.getName()); - Propagator propagator = new MatrixMultiplicationPropagatorPrivateFirst(input1, privacyConstraints[0], - input2, privacyConstraints[1]); + Propagator propagator = new MatrixMultiplicationPropagatorPrivateFirst(input1, privacyConstraints[0], input2, privacyConstraints[1]); mergedPrivacyConstraint = propagator.propagate(); ec.releaseMatrixInput(inst.input1.getName(), inst.input2.getName()); - } else { + } + else { mergedPrivacyConstraint = mergeNary(privacyConstraints, OperatorType.getAggregationType(inst, ec)); inst.setPrivacyConstraint(mergedPrivacyConstraint); } @@ -382,50 +364,44 @@ private static Instruction preprocessAggregateBinaryCPInstruction(AggregateBinar } /** - * Propagates input privacy constraints using general and fine-grained - * constraints depending on the AppendType. - * + * Propagates input privacy constraints using general and fine-grained constraints depending on the AppendType. * @param inst append instruction for which constraints are propagated - * @param ec execution context - * @return instruction with merged privacy constraints propagated to it and - * output CPOperand + * @param ec execution context + * @return instruction with merged privacy constraints propagated to it and output CPOperand */ - private static Instruction preprocessAppendCPInstruction(AppendCPInstruction inst, ExecutionContext ec) { + private static Instruction preprocessAppendCPInstruction(AppendCPInstruction inst, ExecutionContext ec){ PrivacyConstraint[] privacyConstraints = getInputPrivacyConstraints(ec, inst.getInputs()); - if (PrivacyUtils.someConstraintSetBinary(privacyConstraints)) { - if (inst.getAppendType() == AppendCPInstruction.AppendType.STRING) { + if ( PrivacyUtils.someConstraintSetBinary(privacyConstraints) ){ + if ( inst.getAppendType() == AppendCPInstruction.AppendType.STRING ){ PrivacyLevel[] privacyLevels = new PrivacyLevel[2]; privacyLevels[0] = PrivacyUtils.getGeneralPrivacyLevel(privacyConstraints[0]); - privacyLevels[1] = PrivacyUtils.getGeneralPrivacyLevel(privacyConstraints[1]); - PrivacyConstraint outputConstraint = new PrivacyConstraint( - corePropagation(privacyLevels, OperatorType.NonAggregate)); + privacyLevels[1] = PrivacyUtils.getGeneralPrivacyLevel(privacyConstraints[1]); + PrivacyConstraint outputConstraint = new PrivacyConstraint(corePropagation(privacyLevels, OperatorType.NonAggregate)); inst.output.setPrivacyConstraint(outputConstraint); - } else if (inst.getAppendType() == AppendCPInstruction.AppendType.LIST) { + } else if ( inst.getAppendType() == AppendCPInstruction.AppendType.LIST ){ ListObject input1 = (ListObject) ec.getVariable(inst.input1); - if (inst.getOpcode().equals("remove")) { + if ( inst.getOpcode().equals("remove")){ ScalarObject removePosition = ec.getScalarInput(inst.input2); - PropagatorMultiReturn propagator = new ListRemovePropagator(input1, privacyConstraints[0], - removePosition, removePosition.getPrivacyConstraint()); + PropagatorMultiReturn propagator = new ListRemovePropagator(input1, privacyConstraints[0], removePosition, removePosition.getPrivacyConstraint()); PrivacyConstraint[] outputConstraints = propagator.propagate(); inst.output.setPrivacyConstraint(outputConstraints[0]); ((ListAppendRemoveCPInstruction) inst).getOutput2().setPrivacyConstraint(outputConstraints[1]); } else { ListObject input2 = (ListObject) ec.getVariable(inst.input2); - Propagator propagator = new ListAppendPropagator(input1, privacyConstraints[0], input2, - privacyConstraints[1]); + Propagator propagator = new ListAppendPropagator(input1, privacyConstraints[0], input2, privacyConstraints[1]); inst.output.setPrivacyConstraint(propagator.propagate()); } - } else { + } + else { MatrixBlock input1 = ec.getMatrixInput(inst.input1.getName()); MatrixBlock input2 = ec.getMatrixInput(inst.input2.getName()); Propagator propagator; - if (inst.getAppendType() == AppendCPInstruction.AppendType.RBIND) + if ( inst.getAppendType() == AppendCPInstruction.AppendType.RBIND ) propagator = new RBindPropagator(input1, privacyConstraints[0], input2, privacyConstraints[1]); - else if (inst.getAppendType() == AppendCPInstruction.AppendType.CBIND) + else if ( inst.getAppendType() == AppendCPInstruction.AppendType.CBIND ) propagator = new CBindPropagator(input1, privacyConstraints[0], input2, privacyConstraints[1]); - else - throw new DMLPrivacyException("Instruction " + inst.getCPInstructionType() + " with append type " + - inst.getAppendType() + " is not supported by the privacy propagator"); + else throw new DMLPrivacyException("Instruction " + inst.getCPInstructionType() + " with append type " + + inst.getAppendType() + " is not supported by the privacy propagator"); inst.output.setPrivacyConstraint(propagator.propagate()); ec.releaseMatrixInput(inst.input1.getName(), inst.input2.getName()); } @@ -434,44 +410,37 @@ else if (inst.getAppendType() == AppendCPInstruction.AppendType.CBIND) } /** - * Propagates privacy constraints from input to instruction and output CPOperand - * based on given operator type. + * Propagates privacy constraints from input to instruction and output CPOperand based on given operator type. * The propagation is done through the core propagation. - * - * @param inst instruction for which privacy is propagated - * @param ec execution context + * @param inst instruction for which privacy is propagated + * @param ec execution context * @param operatorType defining whether the instruction is aggregating the input - * @return instruction with the merged privacy constraint propagated to it and - * output CPOperand + * @return instruction with the merged privacy constraint propagated to it and output CPOperand */ private static Instruction mergePrivacyConstraintsFromInput(Instruction inst, ExecutionContext ec, - OperatorType operatorType) { - return mergePrivacyConstraintsFromInput(inst, ec, getInputOperands(inst), getOutputOperands(inst), - operatorType); + OperatorType operatorType){ + return mergePrivacyConstraintsFromInput(inst, ec, getInputOperands(inst), getOutputOperands(inst), operatorType); } /** - * Propagates privacy constraints from input to instruction and output CPOperand - * based on given operator type. + * Propagates privacy constraints from input to instruction and output CPOperand based on given operator type. * The propagation is done through the core propagation. - * - * @param inst instruction for which privacy is propagated - * @param ec execution context - * @param inputs to instruction - * @param outputs of instruction + * @param inst instruction for which privacy is propagated + * @param ec execution context + * @param inputs to instruction + * @param outputs of instruction * @param operatorType defining whether the instruction is aggregating the input - * @return instruction with the merged privacy constraint propagated to it and - * output CPOperand + * @return instruction with the merged privacy constraint propagated to it and output CPOperand */ private static Instruction mergePrivacyConstraintsFromInput(Instruction inst, ExecutionContext ec, - CPOperand[] inputs, List outputs, OperatorType operatorType) { - if (inputs != null && inputs.length > 0) { + CPOperand[] inputs, List outputs, OperatorType operatorType){ + if ( inputs != null && inputs.length > 0 ){ PrivacyConstraint[] privacyConstraints = getInputPrivacyConstraints(ec, inputs); - if (privacyConstraints != null) { + if ( privacyConstraints != null ){ PrivacyConstraint mergedPrivacyConstraint = mergeNary(privacyConstraints, operatorType); inst.setPrivacyConstraint(mergedPrivacyConstraint); - for (CPOperand output : outputs) { - if (output != null) { + for ( CPOperand output : outputs ){ + if ( output != null ) { output.setPrivacyConstraint(mergedPrivacyConstraint); } } @@ -481,78 +450,70 @@ private static Instruction mergePrivacyConstraintsFromInput(Instruction inst, Ex } /** - * Throw exception if privacy constraint activated for instruction or for input - * to instruction. - * + * Throw exception if privacy constraint activated for instruction or for input to instruction. * @param inst covariance instruction - * @param ec execution context + * @param ec execution context * @return input instruction if privacy constraints are not activated */ - private static Instruction throwExceptionIfInputOrInstPrivacy(Instruction inst, ExecutionContext ec) { + private static Instruction throwExceptionIfInputOrInstPrivacy(Instruction inst, ExecutionContext ec){ throwExceptionIfPrivacyActivated(inst); CPOperand[] inputOperands = getInputOperands(inst); - if (inputOperands != null) { - for (CPOperand input : inputOperands) { + if (inputOperands != null){ + for ( CPOperand input : inputOperands ){ PrivacyConstraint privacyConstraint = getInputPrivacyConstraint(ec, input); - if (privacyConstraint != null && privacyConstraint.hasConstraints()) { - throw new DMLPrivacyException("Input of instruction " + inst - + " has privacy constraints activated, but the constraints are not propagated during preprocessing of instruction."); + if ( privacyConstraint != null && privacyConstraint.hasConstraints()){ + throw new DMLPrivacyException("Input of instruction " + inst + " has privacy constraints activated, but the constraints are not propagated during preprocessing of instruction."); } } } return inst; } - private static void throwExceptionIfPrivacyActivated(Instruction inst) { - if (inst.getPrivacyConstraint() != null && inst.getPrivacyConstraint().hasConstraints()) { - throw new DMLPrivacyException("Instruction " + inst - + " has privacy constraints activated, but the constraints are not propagated during preprocessing of instruction."); + private static void throwExceptionIfPrivacyActivated(Instruction inst){ + if ( inst.getPrivacyConstraint() != null && inst.getPrivacyConstraint().hasConstraints() ) { + throw new DMLPrivacyException("Instruction " + inst + " has privacy constraints activated, but the constraints are not propagated during preprocessing of instruction."); } } /** * Propagate privacy constraint to instruction and output of instruction - * if data of first input is CacheableData and + * if data of first input is CacheableData and * privacy constraint is activated. - * * @param inst VariableCPInstruction - * @param ec execution context + * @param ec execution context * @return instruction with or without privacy constraints */ - private static Instruction propagateFirstInputPrivacy(VariableCPInstruction inst, ExecutionContext ec) { + private static Instruction propagateFirstInputPrivacy(VariableCPInstruction inst, ExecutionContext ec){ return propagateInputPrivacy(inst, ec, inst.getInput1(), inst.getOutput()); } /** * Propagate privacy constraint to instruction and output of instruction - * if data of second input is CacheableData and + * if data of second input is CacheableData and * privacy constraint is activated. - * * @param inst VariableCPInstruction - * @param ec execution context + * @param ec execution context * @return instruction with or without privacy constraints */ - private static Instruction propagateSecondInputPrivacy(VariableCPInstruction inst, ExecutionContext ec) { + private static Instruction propagateSecondInputPrivacy(VariableCPInstruction inst, ExecutionContext ec){ return propagateInputPrivacy(inst, ec, inst.getInput2(), inst.getOutput()); } /** * Propagate privacy constraint to instruction and output of instruction - * if data of the specified variable is CacheableData + * if data of the specified variable is CacheableData * and privacy constraint is activated - * - * @param inst instruction - * @param ec execution context - * @param inputOperand input from which the privacy constraint is found + * @param inst instruction + * @param ec execution context + * @param inputOperand input from which the privacy constraint is found * @param outputOperand output which the privacy constraint is propagated to * @return instruction with or without privacy constraints */ - private static Instruction propagateInputPrivacy(Instruction inst, ExecutionContext ec, CPOperand inputOperand, - CPOperand outputOperand) { + private static Instruction propagateInputPrivacy(Instruction inst, ExecutionContext ec, CPOperand inputOperand, CPOperand outputOperand){ PrivacyConstraint privacyConstraint = getInputPrivacyConstraint(ec, inputOperand); - if (privacyConstraint != null) { + if ( privacyConstraint != null ) { inst.setPrivacyConstraint(privacyConstraint); - if (outputOperand != null) + if ( outputOperand != null) outputOperand.setPrivacyConstraint(privacyConstraint); } return inst; @@ -560,59 +521,51 @@ private static Instruction propagateInputPrivacy(Instruction inst, ExecutionCont /** * Get privacy constraint of input data variable from execution context. - * - * @param ec execution context from which the data variable is retrieved + * @param ec execution context from which the data variable is retrieved * @param input data variable from which the privacy constraint is retrieved - * @return privacy constraint of variable or null if privacy constraint is not - * set + * @return privacy constraint of variable or null if privacy constraint is not set */ - private static PrivacyConstraint getInputPrivacyConstraint(ExecutionContext ec, CPOperand input) { - if (input != null && input.getName() != null) { + private static PrivacyConstraint getInputPrivacyConstraint(ExecutionContext ec, CPOperand input){ + if ( input != null && input.getName() != null){ Data dd = ec.getVariable(input.getName()); - if (dd != null) + if ( dd != null ) return dd.getPrivacyConstraint(); } return null; } /** - * Returns input privacy constraints as array or returns null if no privacy - * constraints are found in the inputs. - * - * @param ec execution context + * Returns input privacy constraints as array or returns null if no privacy constraints are found in the inputs. + * @param ec execution context * @param inputs from which privacy constraints are retrieved * @return array of privacy constraints from inputs */ - private static PrivacyConstraint[] getInputPrivacyConstraints(ExecutionContext ec, CPOperand[] inputs) { - if (inputs != null && inputs.length > 0) { + private static PrivacyConstraint[] getInputPrivacyConstraints(ExecutionContext ec, CPOperand[] inputs){ + if ( inputs != null && inputs.length > 0){ boolean privacyFound = false; PrivacyConstraint[] privacyConstraints = new PrivacyConstraint[inputs.length]; - for (int i = 0; i < inputs.length; i++) { + for ( int i = 0; i < inputs.length; i++ ){ privacyConstraints[i] = getInputPrivacyConstraint(ec, inputs[i]); - if (privacyConstraints[i] != null) + if ( privacyConstraints[i] != null ) privacyFound = true; } - if (privacyFound) + if ( privacyFound ) return privacyConstraints; } return null; } /** - * Set privacy constraint of data variable with outputName + * Set privacy constraint of data variable with outputName * if the variable exists and the privacy constraint is not null. - * - * @param ec execution context from which the data variable is - * retrieved + * @param ec execution context from which the data variable is retrieved * @param privacyConstraint privacy constraint which the variable should have - * @param outputName name of variable that is retrieved from the - * execution context + * @param outputName name of variable that is retrieved from the execution context */ - private static void setOutputPrivacyConstraint(ExecutionContext ec, PrivacyConstraint privacyConstraint, - String outputName) { - if (privacyConstraint != null) { + private static void setOutputPrivacyConstraint(ExecutionContext ec, PrivacyConstraint privacyConstraint, String outputName){ + if ( privacyConstraint != null ){ Data dd = ec.getVariable(outputName); - if (dd != null) { + if ( dd != null ){ dd.setPrivacyConstraints(privacyConstraint); ec.setVariable(outputName, dd); } @@ -620,54 +573,48 @@ private static void setOutputPrivacyConstraint(ExecutionContext ec, PrivacyConst } /** - * Returns input CPOperands of instruction or returns null if instruction type - * is not supported by this method. - * + * Returns input CPOperands of instruction or returns null if instruction type is not supported by this method. * @param inst instruction from which the inputs are retrieved * @return array of input CPOperands or null */ - private static CPOperand[] getInputOperands(Instruction inst) { - if (inst instanceof ComputationCPInstruction) - return ((ComputationCPInstruction) inst).getInputs(); - if (inst instanceof BuiltinNaryCPInstruction) - return ((BuiltinNaryCPInstruction) inst).getInputs(); - if (inst instanceof FunctionCallCPInstruction) - return ((FunctionCallCPInstruction) inst).getInputs(); - if (inst instanceof SqlCPInstruction) - return ((SqlCPInstruction) inst).getInputs(); - else - return null; + private static CPOperand[] getInputOperands(Instruction inst){ + if ( inst instanceof ComputationCPInstruction ) + return ((ComputationCPInstruction)inst).getInputs(); + if ( inst instanceof BuiltinNaryCPInstruction ) + return ((BuiltinNaryCPInstruction)inst).getInputs(); + if ( inst instanceof FunctionCallCPInstruction ) + return ((FunctionCallCPInstruction)inst).getInputs(); + if ( inst instanceof SqlCPInstruction ) + return ((SqlCPInstruction)inst).getInputs(); + else return null; } /** - * Returns a list of output CPOperands of instruction or an empty list if the - * instruction has no outputs. - * Note that this method needs to be extended as new instruction types are - * added, otherwise it will + * Returns a list of output CPOperands of instruction or an empty list if the instruction has no outputs. + * Note that this method needs to be extended as new instruction types are added, otherwise it will * return an empty list for instructions that may have outputs. - * * @param inst instruction from which the outputs are retrieved * @return list of outputs */ - private static List getOutputOperands(Instruction inst) { + private static List getOutputOperands(Instruction inst){ // The order of the following statements is important - if (inst instanceof MultiReturnParameterizedBuiltinCPInstruction) + if ( inst instanceof MultiReturnParameterizedBuiltinCPInstruction ) return ((MultiReturnParameterizedBuiltinCPInstruction) inst).getOutputs(); - else if (inst instanceof MultiReturnBuiltinCPInstruction) + else if ( inst instanceof MultiReturnBuiltinCPInstruction ) return ((MultiReturnBuiltinCPInstruction) inst).getOutputs(); - else if (inst instanceof ComputationCPInstruction) + else if ( inst instanceof ComputationCPInstruction ) return getSingletonList(((ComputationCPInstruction) inst).getOutput()); - else if (inst instanceof VariableCPInstruction) + else if ( inst instanceof VariableCPInstruction ) return getSingletonList(((VariableCPInstruction) inst).getOutput()); - else if (inst instanceof SqlCPInstruction) + else if ( inst instanceof SqlCPInstruction ) return getSingletonList(((SqlCPInstruction) inst).getOutput()); - else if (inst instanceof BuiltinNaryCPInstruction) - return getSingletonList(((BuiltinNaryCPInstruction) inst).getOutput()); + else if ( inst instanceof BuiltinNaryCPInstruction ) + return getSingletonList(((BuiltinNaryCPInstruction)inst).getOutput()); return new ArrayList<>(); } - private static List getSingletonList(CPOperand operand) { - if (operand != null) + private static List getSingletonList(CPOperand operand){ + if ( operand != null) return new ArrayList<>(Collections.singletonList(operand)); return new ArrayList<>(); } From 10e6462c80613a7139c89989b990e2b3a28adff6 Mon Sep 17 00:00:00 2001 From: Frederic Zoepffel Date: Fri, 9 Feb 2024 00:20:54 +0100 Subject: [PATCH 058/133] second time reverse formatting LibcommonsMath --- .../runtime/matrix/data/LibCommonsMath.java | 58 ++++++++----------- 1 file changed, 25 insertions(+), 33 deletions(-) diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java index bca9adbd010..729c90c922e 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java @@ -108,39 +108,33 @@ public static MatrixBlock[] multiReturnOperations(MatrixBlock in1, MatrixBlock i return multiReturnOperations(in1, in2, opcode, 1, 1); } - public static MatrixBlock[] multiReturnOperations(MatrixBlock in, String opcode, int threads, int num_iterations, - double tol) { - if (opcode.equals("eigen_qr")) + public static MatrixBlock[] multiReturnOperations(MatrixBlock in, String opcode, int threads, int num_iterations, double tol) { + if(opcode.equals("eigen_qr")) return computeEigenQR(in, num_iterations, tol, threads); else return multiReturnOperations(in, opcode, threads, 1); } public static MatrixBlock[] multiReturnOperations(MatrixBlock in, String opcode, int threads, long seed) { - - switch (opcode) { - case "qr": - return computeQR(in); - case "qr2": - return computeQR2(in, threads); - case "lu": - return computeLU(in); - case "eigen": - return computeEigen(in); - case "eigen_lanczos": - return computeEigenLanczos(in, threads, seed); - case "eigen_qr": - return computeEigenQR(in, threads); - case "fft": - return computeFFT(in); - case "ifft": - return computeIFFT(in, null); - case "svd": - return computeSvd(in); - default: - return null; - } - + if(opcode.equals("qr")) + return computeQR(in); + else if (opcode.equals("qr2")) + return computeQR2(in, threads); + else if (opcode.equals("lu")) + return computeLU(in); + else if (opcode.equals("eigen")) + return computeEigen(in); + else if (opcode.equals("eigen_lanczos")) + return computeEigenLanczos(in, threads, seed); + else if (opcode.equals("eigen_qr")) + return computeEigenQR(in, threads); + else if (opcode.equals("svd")) + return computeSvd(in); + else if (opcode.equals("fft")) + return computeFFT(in); + else if (opcode.equals("ifft")) + return computeIFFT(in, null); + return null; } public static MatrixBlock[] multiReturnOperations(MatrixBlock in1, MatrixBlock in2, String opcode, int threads, @@ -156,15 +150,14 @@ public static MatrixBlock[] multiReturnOperations(MatrixBlock in1, MatrixBlock i } public static MatrixBlock matrixMatrixOperations(MatrixBlock in1, MatrixBlock in2, String opcode) { - if (opcode.equals("solve")) { + if(opcode.equals("solve")) { if (in1.getNumRows() != in1.getNumColumns()) throw new DMLRuntimeException("The A matrix, in solve(A,b) should have squared dimensions."); return computeSolve(in1, in2); } - return null; } - + /** * Function to solve a given system of equations. * @@ -332,10 +325,9 @@ private static MatrixBlock[] computeIFFT(MatrixBlock in1, MatrixBlock in2) { /** * Performs Singular Value Decomposition. Calls Apache Commons Math SVD. * X = U * Sigma * Vt, where X is the input matrix, - * U is the left singular matrix, Sigma is the singular values matrix returned - * as a + * U is the left singular matrix, Sigma is the singular values matrix returned as a * column matrix and Vt is the transpose of the right singular matrix V. - * However, the returned array has { U, Sigma, V} + * However, the returned array has { U, Sigma, V} * * @param in Input matrix * @return An array containing U, Sigma & V From 045dcd5894c7694762a083aa8eea8e8b098026d0 Mon Sep 17 00:00:00 2001 From: Frederic Zoepffel Date: Fri, 9 Feb 2024 00:29:39 +0100 Subject: [PATCH 059/133] reverse formatting MultiReturnBuiltinCPInstruction --- .../cp/MultiReturnBuiltinCPInstruction.java | 82 ++++++++++--------- 1 file changed, 44 insertions(+), 38 deletions(-) diff --git a/src/main/java/org/apache/sysds/runtime/instructions/cp/MultiReturnBuiltinCPInstruction.java b/src/main/java/org/apache/sysds/runtime/instructions/cp/MultiReturnBuiltinCPInstruction.java index 26874cc8708..1150f037e8b 100644 --- a/src/main/java/org/apache/sysds/runtime/instructions/cp/MultiReturnBuiltinCPInstruction.java +++ b/src/main/java/org/apache/sysds/runtime/instructions/cp/MultiReturnBuiltinCPInstruction.java @@ -43,51 +43,54 @@ private MultiReturnBuiltinCPInstruction(Operator op, CPOperand input1, ArrayList super(CPType.MultiReturnBuiltin, op, input1, null, outputs.get(0), opcode, istr); _outputs = outputs; } - + public CPOperand getOutput(int i) { return _outputs.get(i); } - public List getOutputs() { + public List getOutputs(){ return _outputs; } - public String[] getOutputNames() { + public String[] getOutputNames(){ return _outputs.parallelStream().map(output -> output.getName()).toArray(String[]::new); } - - public static MultiReturnBuiltinCPInstruction parseInstruction(String str) { + + public static MultiReturnBuiltinCPInstruction parseInstruction ( String str ) { String[] parts = InstructionUtils.getInstructionPartsWithValueType(str); ArrayList outputs = new ArrayList<>(); // first part is always the opcode String opcode = parts[0]; - - if (opcode.equalsIgnoreCase("qr")) { + + if ( opcode.equalsIgnoreCase("qr") ) { // one input and two ouputs CPOperand in1 = new CPOperand(parts[1]); - outputs.add(new CPOperand(parts[2], ValueType.FP64, DataType.MATRIX)); - outputs.add(new CPOperand(parts[3], ValueType.FP64, DataType.MATRIX)); - + outputs.add ( new CPOperand(parts[2], ValueType.FP64, DataType.MATRIX) ); + outputs.add ( new CPOperand(parts[3], ValueType.FP64, DataType.MATRIX) ); + return new MultiReturnBuiltinCPInstruction(null, in1, outputs, opcode, str); - } else if (opcode.equalsIgnoreCase("lu")) { + } + else if ( opcode.equalsIgnoreCase("lu") ) { CPOperand in1 = new CPOperand(parts[1]); - + // one input and three outputs - outputs.add(new CPOperand(parts[2], ValueType.FP64, DataType.MATRIX)); - outputs.add(new CPOperand(parts[3], ValueType.FP64, DataType.MATRIX)); - outputs.add(new CPOperand(parts[4], ValueType.FP64, DataType.MATRIX)); - + outputs.add ( new CPOperand(parts[2], ValueType.FP64, DataType.MATRIX) ); + outputs.add ( new CPOperand(parts[3], ValueType.FP64, DataType.MATRIX) ); + outputs.add ( new CPOperand(parts[4], ValueType.FP64, DataType.MATRIX) ); + return new MultiReturnBuiltinCPInstruction(null, in1, outputs, opcode, str); - - } else if (opcode.equalsIgnoreCase("eigen")) { + + } + else if ( opcode.equalsIgnoreCase("eigen") ) { // one input and two outputs CPOperand in1 = new CPOperand(parts[1]); - outputs.add(new CPOperand(parts[2], ValueType.FP64, DataType.MATRIX)); - outputs.add(new CPOperand(parts[3], ValueType.FP64, DataType.MATRIX)); - + outputs.add ( new CPOperand(parts[2], ValueType.FP64, DataType.MATRIX) ); + outputs.add ( new CPOperand(parts[3], ValueType.FP64, DataType.MATRIX) ); + return new MultiReturnBuiltinCPInstruction(null, in1, outputs, opcode, str); - - } else if (opcode.equalsIgnoreCase("fft")) { + + } + else if (opcode.equalsIgnoreCase("fft")) { // one input and two outputs CPOperand in1 = new CPOperand(parts[1]); outputs.add(new CPOperand(parts[2], ValueType.FP64, DataType.MATRIX)); @@ -95,53 +98,56 @@ public static MultiReturnBuiltinCPInstruction parseInstruction(String str) { return new MultiReturnBuiltinCPInstruction(null, in1, outputs, opcode, str); - } else if (opcode.equalsIgnoreCase("svd")) { + } + else if ( opcode.equalsIgnoreCase("svd") ) { CPOperand in1 = new CPOperand(parts[1]); // one input and three outputs - outputs.add(new CPOperand(parts[2], ValueType.FP64, DataType.MATRIX)); - outputs.add(new CPOperand(parts[3], ValueType.FP64, DataType.MATRIX)); - outputs.add(new CPOperand(parts[4], ValueType.FP64, DataType.MATRIX)); - + outputs.add ( new CPOperand(parts[2], ValueType.FP64, DataType.MATRIX) ); + outputs.add ( new CPOperand(parts[3], ValueType.FP64, DataType.MATRIX) ); + outputs.add ( new CPOperand(parts[4], ValueType.FP64, DataType.MATRIX) ); + return new MultiReturnBuiltinCPInstruction(null, in1, outputs, opcode, str); - } else { + } + else { throw new DMLRuntimeException("Invalid opcode in MultiReturnBuiltin instruction: " + opcode); } } - + public int getNumOutputs() { return _outputs.size(); } - @Override + @Override public void processInstruction(ExecutionContext ec) { - if (!LibCommonsMath.isSupportedMultiReturnOperation(getOpcode())) + if(!LibCommonsMath.isSupportedMultiReturnOperation(getOpcode())) throw new DMLRuntimeException("Invalid opcode in MultiReturnBuiltin instruction: " + getOpcode()); - + MatrixBlock in = ec.getMatrixInput(input1.getName()); MatrixBlock[] out = LibCommonsMath.multiReturnOperations(in, getOpcode()); ec.releaseMatrixInput(input1.getName()); - for (int i = 0; i < _outputs.size(); i++) { + for(int i=0; i < _outputs.size(); i++) { ec.setMatrixOutput(_outputs.get(i).getName(), out[i]); } } - + @Override public boolean hasSingleLineage() { return false; } + @Override @SuppressWarnings("unchecked") public Pair[] getLineageItems(ExecutionContext ec) { LineageItem[] inputLineage = LineageItemUtils.getLineage(ec, input1, input2, input3); - final Pair[] ret = new Pair[_outputs.size()]; - for (int i = 0; i < _outputs.size(); i++) { + final Pair[] ret = new Pair[_outputs.size()]; + for(int i = 0; i < _outputs.size(); i++){ CPOperand out = _outputs.get(i); ret[i] = Pair.of(out.getName(), new LineageItem(getOpcode(), inputLineage)); } - return ret; + return ret; } } From 3f531fff7e0d94382c09f84cb1871615c0f6e382 Mon Sep 17 00:00:00 2001 From: Frederic Zoepffel Date: Fri, 9 Feb 2024 14:59:30 +0100 Subject: [PATCH 060/133] last adjustments --- .gitignore | 3 +++ .../org/apache/sysds/parser/BuiltinFunctionExpression.java | 2 +- .../apache/sysds/runtime/instructions/CPInstructionParser.java | 2 +- .../apache/sysds/runtime/instructions/cp/CPInstruction.java | 2 -- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index 1a83a3a80ed..b296373ea36 100644 --- a/.gitignore +++ b/.gitignore @@ -144,3 +144,6 @@ scripts/perftest/fed/temp src/test/scripts/functions/iogen/*.raw src/test/scripts/functions/pipelines/intermediates/regression/* src/test/scripts/functions/pipelines/intermediates/classification/* + +venv +venv/* \ No newline at end of file diff --git a/src/main/java/org/apache/sysds/parser/BuiltinFunctionExpression.java b/src/main/java/org/apache/sysds/parser/BuiltinFunctionExpression.java index 94a0452f05b..9a2dc041201 100644 --- a/src/main/java/org/apache/sysds/parser/BuiltinFunctionExpression.java +++ b/src/main/java/org/apache/sysds/parser/BuiltinFunctionExpression.java @@ -2136,4 +2136,4 @@ public static Builtins getValueTypeCastOperator( ValueType vt ) { throw new LanguageException("No cast for value type "+vt); } } -} \ No newline at end of file +} diff --git a/src/main/java/org/apache/sysds/runtime/instructions/CPInstructionParser.java b/src/main/java/org/apache/sysds/runtime/instructions/CPInstructionParser.java index 58158991f34..37540da8da1 100644 --- a/src/main/java/org/apache/sysds/runtime/instructions/CPInstructionParser.java +++ b/src/main/java/org/apache/sysds/runtime/instructions/CPInstructionParser.java @@ -397,7 +397,7 @@ public static CPInstruction parseSingleInstruction ( CPType cptype, String str ) return ReorgCPInstruction.parseInstruction(str); case Dnn: - return DnnCPInstruction.parseInstruction(str); + return DnnCPInstruction.parseInstruction(str); case UaggOuterChain: return UaggOuterChainCPInstruction.parseInstruction(str); diff --git a/src/main/java/org/apache/sysds/runtime/instructions/cp/CPInstruction.java b/src/main/java/org/apache/sysds/runtime/instructions/cp/CPInstruction.java index aaf8227e25d..d44bae285e9 100644 --- a/src/main/java/org/apache/sysds/runtime/instructions/cp/CPInstruction.java +++ b/src/main/java/org/apache/sysds/runtime/instructions/cp/CPInstruction.java @@ -38,9 +38,7 @@ public abstract class CPInstruction extends Instruction { protected static final Log LOG = LogFactory.getLog(CPInstruction.class.getName()); - public enum CPType { - AggregateUnary, AggregateBinary, AggregateTernary, Unary, Binary, Ternary, Quaternary, BuiltinNary, Ctable, MultiReturnParameterizedBuiltin, ParameterizedBuiltin, MultiReturnBuiltin, MultiReturnComplexMatrixBuiltin, From f34f02ccbc0c4d1e241a233b1da2b00105ababc6 Mon Sep 17 00:00:00 2001 From: Jessica Priebe Date: Thu, 15 Feb 2024 16:33:58 +0100 Subject: [PATCH 061/133] added linearized fft and ifft --- .../runtime/matrix/data/LibMatrixFourier.java | 101 +++++++++++++++--- 1 file changed, 85 insertions(+), 16 deletions(-) diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java index 7285fd14e2d..610d0104888 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java @@ -24,7 +24,7 @@ public class LibMatrixFourier { /** - * Function to perform FFT on two given matrices. + * Function to perform FFT for two given matrices. * The first one represents the real values and the second one the imaginary values. * The output also contains one matrix for the real and one for the imaginary values. * @@ -38,13 +38,13 @@ public static MatrixBlock[] fft(MatrixBlock re, MatrixBlock im){ int cols = re.getNumColumns(); if(!isPowerOfTwo(rows) || !isPowerOfTwo(cols)) throw new RuntimeException("false dimensions"); - fft(re.getDenseBlockValues(), im.getDenseBlockValues(), rows, cols); + fft(re.getDenseBlockValues(), im.getDenseBlockValues(), rows, cols, true); return new MatrixBlock[]{re, im}; } /** - * Function to perform IFFT on two given matrices. + * Function to perform IFFT for two given matrices. * The first one represents the real values and the second one the imaginary values. * The output also contains one matrix for the real and one for the imaginary values. * @@ -58,13 +58,53 @@ public static MatrixBlock[] ifft(MatrixBlock re, MatrixBlock im){ int cols = re.getNumColumns(); if(!isPowerOfTwo(rows) || !isPowerOfTwo(cols)) throw new RuntimeException("false dimensions"); - ifft(re.getDenseBlockValues(), im.getDenseBlockValues(), rows, cols); + ifft(re.getDenseBlockValues(), im.getDenseBlockValues(), rows, cols, true); return new MatrixBlock[]{re, im}; } /** - * Function to perform FFT on two given double arrays. + * Function to perform FFT for each row of two given matrices. + * The first one represents the real values and the second one the imaginary values. + * The output also contains one matrix for the real and one for the imaginary values. + * + * @param re matrix object representing the real values + * @param im matrix object representing the imaginary values + * @return array of two matrix blocks + */ + public static MatrixBlock[] fft_linearized(MatrixBlock re, MatrixBlock im){ + + int rows = re.getNumRows(); + int cols = re.getNumColumns(); + if(!isPowerOfTwo(rows) || !isPowerOfTwo(cols)) throw new RuntimeException("false dimensions"); + + fft(re.getDenseBlockValues(), im.getDenseBlockValues(), rows, cols, false); + + return new MatrixBlock[]{re, im}; + } + + /** + * Function to perform IFFT for each row of two given matrices. + * The first one represents the real values and the second one the imaginary values. + * The output also contains one matrix for the real and one for the imaginary values. + * + * @param re matrix object representing the real values + * @param im matrix object representing the imaginary values + * @return array of two matrix blocks + */ + public static MatrixBlock[] ifft_linearized(MatrixBlock re, MatrixBlock im){ + + int rows = re.getNumRows(); + int cols = re.getNumColumns(); + if(!isPowerOfTwo(rows) || !isPowerOfTwo(cols)) throw new RuntimeException("false dimensions"); + + ifft(re.getDenseBlockValues(), im.getDenseBlockValues(), rows, cols, false); + + return new MatrixBlock[]{re, im}; + } + + /** + * Function to perform FFT for two given double arrays. * The first one represents the real values and the second one the imaginary values. * Both arrays get updated and contain the result. * @@ -72,8 +112,9 @@ public static MatrixBlock[] ifft(MatrixBlock re, MatrixBlock im){ * @param im array representing the imaginary values * @param rows number of rows * @param cols number of rows + * @param inclColCalc if true, fft is also calculated for each column, otherwise only for each row */ - public static void fft(double[] re, double[] im, int rows, int cols) { + public static void fft(double[] re, double[] im, int rows, int cols, boolean inclColCalc) { double[] re_inter = new double[rows*cols]; double[] im_inter = new double[rows*cols]; @@ -82,14 +123,16 @@ public static void fft(double[] re, double[] im, int rows, int cols) { fft_one_dim(re, im, re_inter, im_inter, i*cols, (i+1)*cols, cols, 1); } - for(int j = 0; j < cols; j++){ - fft_one_dim(re, im, re_inter, im_inter, j, j+rows*cols, rows, cols); + if(inclColCalc){ + for(int j = 0; j < cols; j++){ + fft_one_dim(re, im, re_inter, im_inter, j, j+rows*cols, rows, cols); + } } } /** - * Function to perform IFFT on two given double arrays. + * Function to perform IFFT for two given double arrays. * The first one represents the real values and the second one the imaginary values. * Both arrays get updated and contain the result. * @@ -97,15 +140,17 @@ public static void fft(double[] re, double[] im, int rows, int cols) { * @param im array representing the imaginary values * @param rows number of rows * @param cols number of rows + * @param inclColCalc if true, fft is also calculated for each column, otherwise only for each row */ - public static void ifft(double[] re, double[] im, int rows, int cols) { + public static void ifft(double[] re, double[] im, int rows, int cols, boolean inclColCalc) { double[] re_inter = new double[rows*cols]; double[] im_inter = new double[rows*cols]; - for(int j = 0; j < cols; j++){ - ifft_one_dim(re, im, re_inter, im_inter, j, j+rows*cols, rows, cols); - + if(inclColCalc) { + for (int j = 0; j < cols; j++) { + ifft_one_dim(re, im, re_inter, im_inter, j, j + rows * cols, rows, cols); + } } for(int i = 0; i < rows; i++){ @@ -115,7 +160,7 @@ public static void ifft(double[] re, double[] im, int rows, int cols) { } /** - * Function to perform one-dimensional FFT on two given double arrays. The first one represents the real values and + * Function to perform one-dimensional FFT for two given double arrays. The first one represents the real values and * the second one the imaginary values. Both arrays get updated and contain the result. * * @param re array representing the real values @@ -211,7 +256,7 @@ public static boolean isPowerOfTwo(int n){ } /** - * Function to perform FFT on a given matrices. + * Function to perform FFT for a given matrix. * The given matrix only represents real values. * The output contains one matrix for the real and one for the imaginary values. * @@ -223,7 +268,7 @@ public static MatrixBlock[] fft(MatrixBlock re){ } /** - * Function to perform IFFT on a given matrices. + * Function to perform IFFT for a given matrix. * The given matrix only represents real values. * The output contains one matrix for the real and one for the imaginary values. * @@ -234,4 +279,28 @@ public static MatrixBlock[] ifft(MatrixBlock re){ return ifft(re, new MatrixBlock(re.getNumRows(),re.getNumColumns(), new double[re.getNumRows()*re.getNumColumns()])); } + /** + * Function to perform FFT for each row of a given matrix. + * The given matrix only represents real values. + * The output contains one matrix for the real and one for the imaginary values. + * + * @param re matrix object representing the real values + * @return array of two matrix blocks + */ + public static MatrixBlock[] fft_linearized(MatrixBlock re){ + return fft_linearized(re, new MatrixBlock(re.getNumRows(),re.getNumColumns(), new double[re.getNumRows()*re.getNumColumns()])); + } + + /** + * Function to perform IFFT for each row of a given matrix. + * The given matrix only represents real values. + * The output contains one matrix for the real and one for the imaginary values. + * + * @param re matrix object representing the real values + * @return array of two matrix blocks + */ + public static MatrixBlock[] ifft_linearized(MatrixBlock re){ + return ifft_linearized(re, new MatrixBlock(re.getNumRows(),re.getNumColumns(), new double[re.getNumRows()*re.getNumColumns()])); + } + } From 25e4168906dc809bf61db529082364863a85b576 Mon Sep 17 00:00:00 2001 From: Jessica Priebe Date: Thu, 15 Feb 2024 16:41:05 +0100 Subject: [PATCH 062/133] added formatting for LibMatrixFourier and FourierTest --- .../runtime/matrix/data/LibMatrixFourier.java | 177 ++++++------ .../test/component/matrix/FourierTest.java | 251 ++++++++---------- 2 files changed, 198 insertions(+), 230 deletions(-) diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java index 610d0104888..4c0c10c7021 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java @@ -24,137 +24,137 @@ public class LibMatrixFourier { /** - * Function to perform FFT for two given matrices. - * The first one represents the real values and the second one the imaginary values. - * The output also contains one matrix for the real and one for the imaginary values. + * Function to perform FFT for two given matrices. The first one represents the real values and the second one the + * imaginary values. The output also contains one matrix for the real and one for the imaginary values. * * @param re matrix object representing the real values * @param im matrix object representing the imaginary values * @return array of two matrix blocks */ - public static MatrixBlock[] fft(MatrixBlock re, MatrixBlock im){ + public static MatrixBlock[] fft(MatrixBlock re, MatrixBlock im) { int rows = re.getNumRows(); int cols = re.getNumColumns(); - if(!isPowerOfTwo(rows) || !isPowerOfTwo(cols)) throw new RuntimeException("false dimensions"); + if(!isPowerOfTwo(rows) || !isPowerOfTwo(cols)) + throw new RuntimeException("false dimensions"); fft(re.getDenseBlockValues(), im.getDenseBlockValues(), rows, cols, true); - return new MatrixBlock[]{re, im}; + return new MatrixBlock[] {re, im}; } /** - * Function to perform IFFT for two given matrices. - * The first one represents the real values and the second one the imaginary values. - * The output also contains one matrix for the real and one for the imaginary values. + * Function to perform IFFT for two given matrices. The first one represents the real values and the second one the + * imaginary values. The output also contains one matrix for the real and one for the imaginary values. * * @param re matrix object representing the real values * @param im matrix object representing the imaginary values * @return array of two matrix blocks */ - public static MatrixBlock[] ifft(MatrixBlock re, MatrixBlock im){ + public static MatrixBlock[] ifft(MatrixBlock re, MatrixBlock im) { int rows = re.getNumRows(); int cols = re.getNumColumns(); - if(!isPowerOfTwo(rows) || !isPowerOfTwo(cols)) throw new RuntimeException("false dimensions"); + if(!isPowerOfTwo(rows) || !isPowerOfTwo(cols)) + throw new RuntimeException("false dimensions"); ifft(re.getDenseBlockValues(), im.getDenseBlockValues(), rows, cols, true); - return new MatrixBlock[]{re, im}; + return new MatrixBlock[] {re, im}; } /** - * Function to perform FFT for each row of two given matrices. - * The first one represents the real values and the second one the imaginary values. - * The output also contains one matrix for the real and one for the imaginary values. + * Function to perform FFT for each row of two given matrices. The first one represents the real values and the + * second one the imaginary values. The output also contains one matrix for the real and one for the imaginary + * values. * * @param re matrix object representing the real values * @param im matrix object representing the imaginary values * @return array of two matrix blocks */ - public static MatrixBlock[] fft_linearized(MatrixBlock re, MatrixBlock im){ + public static MatrixBlock[] fft_linearized(MatrixBlock re, MatrixBlock im) { int rows = re.getNumRows(); int cols = re.getNumColumns(); - if(!isPowerOfTwo(rows) || !isPowerOfTwo(cols)) throw new RuntimeException("false dimensions"); + if(!isPowerOfTwo(rows) || !isPowerOfTwo(cols)) + throw new RuntimeException("false dimensions"); fft(re.getDenseBlockValues(), im.getDenseBlockValues(), rows, cols, false); - return new MatrixBlock[]{re, im}; + return new MatrixBlock[] {re, im}; } /** - * Function to perform IFFT for each row of two given matrices. - * The first one represents the real values and the second one the imaginary values. - * The output also contains one matrix for the real and one for the imaginary values. + * Function to perform IFFT for each row of two given matrices. The first one represents the real values and the + * second one the imaginary values. The output also contains one matrix for the real and one for the imaginary + * values. * * @param re matrix object representing the real values * @param im matrix object representing the imaginary values * @return array of two matrix blocks */ - public static MatrixBlock[] ifft_linearized(MatrixBlock re, MatrixBlock im){ + public static MatrixBlock[] ifft_linearized(MatrixBlock re, MatrixBlock im) { int rows = re.getNumRows(); int cols = re.getNumColumns(); - if(!isPowerOfTwo(rows) || !isPowerOfTwo(cols)) throw new RuntimeException("false dimensions"); + if(!isPowerOfTwo(rows) || !isPowerOfTwo(cols)) + throw new RuntimeException("false dimensions"); ifft(re.getDenseBlockValues(), im.getDenseBlockValues(), rows, cols, false); - return new MatrixBlock[]{re, im}; + return new MatrixBlock[] {re, im}; } /** - * Function to perform FFT for two given double arrays. - * The first one represents the real values and the second one the imaginary values. - * Both arrays get updated and contain the result. + * Function to perform FFT for two given double arrays. The first one represents the real values and the second one + * the imaginary values. Both arrays get updated and contain the result. * - * @param re array representing the real values - * @param im array representing the imaginary values - * @param rows number of rows - * @param cols number of rows + * @param re array representing the real values + * @param im array representing the imaginary values + * @param rows number of rows + * @param cols number of rows * @param inclColCalc if true, fft is also calculated for each column, otherwise only for each row */ public static void fft(double[] re, double[] im, int rows, int cols, boolean inclColCalc) { - double[] re_inter = new double[rows*cols]; - double[] im_inter = new double[rows*cols]; + double[] re_inter = new double[rows * cols]; + double[] im_inter = new double[rows * cols]; - for(int i = 0; i < rows; i++){ - fft_one_dim(re, im, re_inter, im_inter, i*cols, (i+1)*cols, cols, 1); + for(int i = 0; i < rows; i++) { + fft_one_dim(re, im, re_inter, im_inter, i * cols, (i + 1) * cols, cols, 1); } - if(inclColCalc){ - for(int j = 0; j < cols; j++){ - fft_one_dim(re, im, re_inter, im_inter, j, j+rows*cols, rows, cols); + if(inclColCalc) { + for(int j = 0; j < cols; j++) { + fft_one_dim(re, im, re_inter, im_inter, j, j + rows * cols, rows, cols); } } } /** - * Function to perform IFFT for two given double arrays. - * The first one represents the real values and the second one the imaginary values. - * Both arrays get updated and contain the result. + * Function to perform IFFT for two given double arrays. The first one represents the real values and the second one + * the imaginary values. Both arrays get updated and contain the result. * - * @param re array representing the real values - * @param im array representing the imaginary values - * @param rows number of rows - * @param cols number of rows + * @param re array representing the real values + * @param im array representing the imaginary values + * @param rows number of rows + * @param cols number of rows * @param inclColCalc if true, fft is also calculated for each column, otherwise only for each row */ public static void ifft(double[] re, double[] im, int rows, int cols, boolean inclColCalc) { - double[] re_inter = new double[rows*cols]; - double[] im_inter = new double[rows*cols]; + double[] re_inter = new double[rows * cols]; + double[] im_inter = new double[rows * cols]; if(inclColCalc) { - for (int j = 0; j < cols; j++) { + for(int j = 0; j < cols; j++) { ifft_one_dim(re, im, re_inter, im_inter, j, j + rows * cols, rows, cols); } } - for(int i = 0; i < rows; i++){ - ifft_one_dim(re, im, re_inter, im_inter, i*cols, (i+1)*cols, cols, 1); + for(int i = 0; i < rows; i++) { + ifft_one_dim(re, im, re_inter, im_inter, i * cols, (i + 1) * cols, cols, 1); } } @@ -176,25 +176,27 @@ public static void fft_one_dim(double[] re, double[] im, double[] re_inter, doub int num, int minStep) { // start inclusive, stop exclusive - if(num == 1) return; + if(num == 1) + return; // even indices - for(int step = minStep*(num/2), subNum = 2; subNum <= num; step/=2, subNum*=2){ + for(int step = minStep * (num / 2), subNum = 2; subNum <= num; step /= 2, subNum *= 2) { - double angle = -2*FastMath.PI/subNum; + double angle = -2 * FastMath.PI / subNum; // use ceil for the main (sub)array - for(int sub = 0; sub < FastMath.ceil(num/(2*(double)subNum)); sub++){ + for(int sub = 0; sub < FastMath.ceil(num / (2 * (double) subNum)); sub++) { for(int isOdd = 0; isOdd < 2; isOdd++) { // no odd values for main (sub)array - if (isOdd == 1 && subNum == num) return; + if(isOdd == 1 && subNum == num) + return; - int startSub = start + sub*minStep + isOdd*(step/2); + int startSub = start + sub * minStep + isOdd * (step / 2); // first iterates over even indices, then over odd indices - for (int j = startSub, cnt = 0; cnt < subNum / 2; j += 2*step, cnt++) { + for(int j = startSub, cnt = 0; cnt < subNum / 2; j += 2 * step, cnt++) { double omega_pow_re = FastMath.cos(cnt * angle); double omega_pow_im = FastMath.sin(cnt * angle); @@ -205,13 +207,13 @@ public static void fft_one_dim(double[] re, double[] im, double[] re_inter, doub int index = startSub + cnt * step; re_inter[index] = re[j] + m_re; - re_inter[index + (stop-start)/2] = re[j] - m_re; + re_inter[index + (stop - start) / 2] = re[j] - m_re; im_inter[index] = im[j] + m_im; - im_inter[index + (stop-start)/2] = im[j] - m_im; + im_inter[index + (stop - start) / 2] = im[j] - m_im; } - for (int j = startSub; j < startSub + (stop-start); j += step) { + for(int j = startSub; j < startSub + (stop - start); j += step) { re[j] = re_inter[j]; im[j] = im_inter[j]; re_inter[j] = 0; @@ -226,21 +228,22 @@ public static void fft_one_dim(double[] re, double[] im, double[] re_inter, doub } - private static void ifft_one_dim(double[] re, double[] im, double[] re_inter, double[] im_inter, int start, int stop, int num, int minStep) { + private static void ifft_one_dim(double[] re, double[] im, double[] re_inter, double[] im_inter, int start, + int stop, int num, int minStep) { // conjugate input - for (int i = start; i < start+num*minStep; i+=minStep){ + for(int i = start; i < start + num * minStep; i += minStep) { im[i] = -im[i]; } // apply fft - //fft_one_dim_recursive(re, im, re_inter, im_inter, start, step, num, subArraySize); + // fft_one_dim_recursive(re, im, re_inter, im_inter, start, step, num, subArraySize); fft_one_dim(re, im, re_inter, im_inter, start, stop, num, minStep); // conjugate and scale result - for (int i = start; i < start+num*minStep; i+=minStep){ - re[i] = re[i]/num; - im[i] = -im[i]/num; + for(int i = start; i < start + num * minStep; i += minStep) { + re[i] = re[i] / num; + im[i] = -im[i] / num; } } @@ -251,56 +254,56 @@ private static void ifft_one_dim(double[] re, double[] im, double[] re_inter, do * @param n integer to check * @return true if n is a power of two, false otherwise */ - public static boolean isPowerOfTwo(int n){ + public static boolean isPowerOfTwo(int n) { return (n != 0) && ((n & (n - 1)) == 0); } /** - * Function to perform FFT for a given matrix. - * The given matrix only represents real values. - * The output contains one matrix for the real and one for the imaginary values. + * Function to perform FFT for a given matrix. The given matrix only represents real values. The output contains one + * matrix for the real and one for the imaginary values. * * @param re matrix object representing the real values * @return array of two matrix blocks */ - public static MatrixBlock[] fft(MatrixBlock re){ - return fft(re, new MatrixBlock(re.getNumRows(),re.getNumColumns(), new double[re.getNumRows()*re.getNumColumns()])); + public static MatrixBlock[] fft(MatrixBlock re) { + return fft(re, + new MatrixBlock(re.getNumRows(), re.getNumColumns(), new double[re.getNumRows() * re.getNumColumns()])); } /** - * Function to perform IFFT for a given matrix. - * The given matrix only represents real values. - * The output contains one matrix for the real and one for the imaginary values. + * Function to perform IFFT for a given matrix. The given matrix only represents real values. The output contains + * one matrix for the real and one for the imaginary values. * * @param re matrix object representing the real values * @return array of two matrix blocks */ - public static MatrixBlock[] ifft(MatrixBlock re){ - return ifft(re, new MatrixBlock(re.getNumRows(),re.getNumColumns(), new double[re.getNumRows()*re.getNumColumns()])); + public static MatrixBlock[] ifft(MatrixBlock re) { + return ifft(re, + new MatrixBlock(re.getNumRows(), re.getNumColumns(), new double[re.getNumRows() * re.getNumColumns()])); } /** - * Function to perform FFT for each row of a given matrix. - * The given matrix only represents real values. - * The output contains one matrix for the real and one for the imaginary values. + * Function to perform FFT for each row of a given matrix. The given matrix only represents real values. The output + * contains one matrix for the real and one for the imaginary values. * * @param re matrix object representing the real values * @return array of two matrix blocks */ - public static MatrixBlock[] fft_linearized(MatrixBlock re){ - return fft_linearized(re, new MatrixBlock(re.getNumRows(),re.getNumColumns(), new double[re.getNumRows()*re.getNumColumns()])); + public static MatrixBlock[] fft_linearized(MatrixBlock re) { + return fft_linearized(re, + new MatrixBlock(re.getNumRows(), re.getNumColumns(), new double[re.getNumRows() * re.getNumColumns()])); } /** - * Function to perform IFFT for each row of a given matrix. - * The given matrix only represents real values. - * The output contains one matrix for the real and one for the imaginary values. + * Function to perform IFFT for each row of a given matrix. The given matrix only represents real values. The output + * contains one matrix for the real and one for the imaginary values. * * @param re matrix object representing the real values * @return array of two matrix blocks */ - public static MatrixBlock[] ifft_linearized(MatrixBlock re){ - return ifft_linearized(re, new MatrixBlock(re.getNumRows(),re.getNumColumns(), new double[re.getNumRows()*re.getNumColumns()])); + public static MatrixBlock[] ifft_linearized(MatrixBlock re) { + return ifft_linearized(re, + new MatrixBlock(re.getNumRows(), re.getNumColumns(), new double[re.getNumRows() * re.getNumColumns()])); } } diff --git a/src/test/java/org/apache/sysds/test/component/matrix/FourierTest.java b/src/test/java/org/apache/sysds/test/component/matrix/FourierTest.java index 2b25b54c7e5..ea279ef0bcf 100644 --- a/src/test/java/org/apache/sysds/test/component/matrix/FourierTest.java +++ b/src/test/java/org/apache/sysds/test/component/matrix/FourierTest.java @@ -32,8 +32,8 @@ public class FourierTest { @Test public void test_fft_one_dim() { - MatrixBlock re = new MatrixBlock(1, 4, new double[]{0, 18, -15, 3}); - MatrixBlock im = new MatrixBlock(1, 4, new double[4]); + MatrixBlock re = new MatrixBlock(1, 4, new double[] {0, 18, -15, 3}); + MatrixBlock im = new MatrixBlock(1, 4, new double[4]); double[] expected_re = {6, 15, -36, 15}; double[] expected_im = {0, -15, 0, 15}; @@ -48,8 +48,8 @@ public void test_fft_one_dim() { @Test public void test_fft_one_dim_2() { - MatrixBlock re = new MatrixBlock(1, 8, new double[]{0, 18, -15, 3, 5, 10, 5, 9}); - MatrixBlock im = new MatrixBlock(1, 8, new double[8]); + MatrixBlock re = new MatrixBlock(1, 8, new double[] {0, 18, -15, 3, 5, 10, 5, 9}); + MatrixBlock im = new MatrixBlock(1, 8, new double[8]); double[] expected_re = {35, 4.89949, 15, -14.89949, -45, -14.89949, 15, 4.89949}; double[] expected_im = {0, 18.58579, -16, -21.41421, 0, 21.41421, 16, -18.58579}; @@ -63,8 +63,8 @@ public void test_fft_one_dim_2() { @Test public void test_fft_one_dim_matrixBlock() { - MatrixBlock re = new MatrixBlock(1, 4, new double[]{0, 18, -15, 3}); - MatrixBlock im = new MatrixBlock(1, 4, new double[]{0, 0, 0, 0}); + MatrixBlock re = new MatrixBlock(1, 4, new double[] {0, 18, -15, 3}); + MatrixBlock im = new MatrixBlock(1, 4, new double[] {0, 0, 0, 0}); double[] expected_re = {6, 15, -36, 15}; double[] expected_im = {0, -15, 0, 15}; @@ -79,8 +79,8 @@ public void test_fft_one_dim_matrixBlock() { @Test public void test_ifft_one_dim_matrixBlock_2() { - double[] in_re = new double[]{1, -2, 3, -4}; - double[] in_im = new double[]{0, 0, 0, 0}; + double[] in_re = new double[] {1, -2, 3, -4}; + double[] in_im = new double[] {0, 0, 0, 0}; MatrixBlock re = new MatrixBlock(1, 4, in_re); MatrixBlock im = new MatrixBlock(1, 4, in_im); @@ -96,10 +96,10 @@ public void test_ifft_one_dim_matrixBlock_2() { @Test public void test_fft_two_dim_matrixBlock() { - MatrixBlock re = new MatrixBlock(2, 2, new double[]{0, 18, -15, 3}); - MatrixBlock im = new MatrixBlock(2, 2, new double[]{0, 0, 0, 0}); + MatrixBlock re = new MatrixBlock(2, 2, new double[] {0, 18, -15, 3}); + MatrixBlock im = new MatrixBlock(2, 2, new double[] {0, 0, 0, 0}); - double[] expected_re = {6,-36, 30, 0}; + double[] expected_re = {6, -36, 30, 0}; double[] expected_im = {0, 0, 0, 0}; MatrixBlock[] res = fft(re, im); @@ -112,8 +112,8 @@ public void test_fft_two_dim_matrixBlock() { @Test public void test_ifft_two_dim_matrixBlock() { - MatrixBlock re = new MatrixBlock(2, 2, new double[]{6,-36, 30, 0}); - MatrixBlock im = new MatrixBlock(2, 2, new double[]{0, 0, 0, 0}); + MatrixBlock re = new MatrixBlock(2, 2, new double[] {6, -36, 30, 0}); + MatrixBlock im = new MatrixBlock(2, 2, new double[] {0, 0, 0, 0}); double[] expected_re = {0, 18, -15, 3}; double[] expected_im = {0, 0, 0, 0}; @@ -128,8 +128,8 @@ public void test_ifft_two_dim_matrixBlock() { @Test public void test_fft_two_dim_matrixBlock_row_1() { - MatrixBlock re = new MatrixBlock(1, 2, new double[]{0, 18}); - MatrixBlock im = new MatrixBlock(1, 2, new double[]{0, 0}); + MatrixBlock re = new MatrixBlock(1, 2, new double[] {0, 18}); + MatrixBlock im = new MatrixBlock(1, 2, new double[] {0, 0}); double[] expected_re = {18, -18}; double[] expected_im = {0, 0}; @@ -144,8 +144,8 @@ public void test_fft_two_dim_matrixBlock_row_1() { @Test public void test_fft_two_dim_matrixBlock_row_2() { - MatrixBlock re = new MatrixBlock(1, 2, new double[]{ -15, 3}); - MatrixBlock im = new MatrixBlock(1, 2, new double[]{0, 0}); + MatrixBlock re = new MatrixBlock(1, 2, new double[] {-15, 3}); + MatrixBlock im = new MatrixBlock(1, 2, new double[] {0, 0}); double[] expected_re = {-12, -18}; double[] expected_im = {0, 0}; @@ -161,28 +161,23 @@ public void test_fft_two_dim_matrixBlock_row_2() { public void test_ifft_with_complex_numpy_data() { // removed 0's at the end, not just real - MatrixBlock re = new MatrixBlock(1, 16, new double[]{ - 0.5398705320215192, 0.1793355360736929, 0.9065254044489506, 0.45004385530909075, - 0.11128090341119468, 0.11742862805303522, 0.7574827475752481, 0.2778193170985158, - 0.9251562928110273, 0.9414429667551927, 0.45131569795507087, 0.9522067687409731, - 0.22491032260636257, 0.6579426733967295, 0.7021558730366062, 0.7861117825617701, - }); - - MatrixBlock im = new MatrixBlock(1, 16, new double[16]); - - double[] expected_re = { - 0.5613143313659362, -0.020146500453061336, 0.07086545895481336, -0.05003801442765281, - -0.06351635451036074, -0.03346768844048936, 0.07023899089706032, 0.007330763123826495, - 0.016022890367311193, 0.007330763123826495, 0.07023899089706032, -0.03346768844048936, - -0.06351635451036074, -0.05003801442765281, 0.07086545895481336, -0.020146500453061336 - }; + MatrixBlock re = new MatrixBlock(1, 16, + new double[] {0.5398705320215192, 0.1793355360736929, 0.9065254044489506, 0.45004385530909075, + 0.11128090341119468, 0.11742862805303522, 0.7574827475752481, 0.2778193170985158, 0.9251562928110273, + 0.9414429667551927, 0.45131569795507087, 0.9522067687409731, 0.22491032260636257, 0.6579426733967295, + 0.7021558730366062, 0.7861117825617701,}); - double[] expected_im = { - 0.0, -0.07513090216687965, 0.023854392878864396, -0.018752997939582024, - -0.035626994964481226, -0.07808216015046443, 0.036579082654843526, -0.10605270957897009, - 0.0, 0.10605270957897009, -0.036579082654843526, 0.07808216015046443, - 0.035626994964481226, 0.018752997939582024, -0.023854392878864396, 0.07513090216687965 - }; + MatrixBlock im = new MatrixBlock(1, 16, new double[16]); + + double[] expected_re = {0.5613143313659362, -0.020146500453061336, 0.07086545895481336, -0.05003801442765281, + -0.06351635451036074, -0.03346768844048936, 0.07023899089706032, 0.007330763123826495, 0.016022890367311193, + 0.007330763123826495, 0.07023899089706032, -0.03346768844048936, -0.06351635451036074, -0.05003801442765281, + 0.07086545895481336, -0.020146500453061336}; + + double[] expected_im = {0.0, -0.07513090216687965, 0.023854392878864396, -0.018752997939582024, + -0.035626994964481226, -0.07808216015046443, 0.036579082654843526, -0.10605270957897009, 0.0, + 0.10605270957897009, -0.036579082654843526, 0.07808216015046443, 0.035626994964481226, 0.018752997939582024, + -0.023854392878864396, 0.07513090216687965}; MatrixBlock[] res = ifft(re, im); @@ -194,22 +189,16 @@ public void test_ifft_with_complex_numpy_data() { @Test public void test_ifft_2d_with_generated_data() { - MatrixBlock re = new MatrixBlock(2, 2, new double[]{ - 0.6749989259154331, 0.6278845555308362, 0.995916990652601, 0.511472971081564 - }); + MatrixBlock re = new MatrixBlock(2, 2, + new double[] {0.6749989259154331, 0.6278845555308362, 0.995916990652601, 0.511472971081564}); - MatrixBlock im = new MatrixBlock(2, 2, new double[]{ - 0.8330832079105173, 0.09857986129294982, 0.6808883894146879, 0.28353782431047303 - }); + MatrixBlock im = new MatrixBlock(2, 2, + new double[] {0.8330832079105173, 0.09857986129294982, 0.6808883894146879, 0.28353782431047303}); // adjusted the expected output - double[] expected_re = { - 0.70256836, 0.1328896, -0.05112662, -0.10933241 - }; + double[] expected_re = {0.70256836, 0.1328896, -0.05112662, -0.10933241}; - double[] expected_im = { - 0.47402232, 0.28296348, -0.00819079, 0.0842882 - }; + double[] expected_im = {0.47402232, 0.28296348, -0.00819079, 0.0842882}; MatrixBlock[] res = ifft(re, im); @@ -222,28 +211,21 @@ public void test_ifft_2d_with_generated_data() { public void test_ifft_with_real_numpy_data() { // not complex - MatrixBlock re = new MatrixBlock(1, 16, new double[]{ - 0.17768499045697306, 0.3405035491673728, 0.9272906450602005, 0.28247504052271843, - 0.42775517613102865, 0.8338783039818357, 0.9813749624557385, 0.47224489612381737, - 0.7936831995784907, 0.5584182145651306, 0.5296113722056018, 0.6687593295928902, - 0.9630598447622862, 0.7130539473424196, 0.860081483892192, 0.8985058305053549 - }); + MatrixBlock re = new MatrixBlock(1, 16, + new double[] {0.17768499045697306, 0.3405035491673728, 0.9272906450602005, 0.28247504052271843, + 0.42775517613102865, 0.8338783039818357, 0.9813749624557385, 0.47224489612381737, 0.7936831995784907, + 0.5584182145651306, 0.5296113722056018, 0.6687593295928902, 0.9630598447622862, 0.7130539473424196, + 0.860081483892192, 0.8985058305053549}); - MatrixBlock im = new MatrixBlock(1, 16, new double[16]); + MatrixBlock im = new MatrixBlock(1, 16, new double[16]); // adjusted the expected output - double[] expected_re = { - 0.6517738, -0.0263837 , -0.03631354, -0.01644966, - -0.05851095, -0.0849794, -0.01611732, -0.02618679, - 0.05579391, -0.02618679, -0.01611732, -0.0849794, - -0.05851095, -0.01644966, -0.03631354, -0.0263837 - }; + double[] expected_re = {0.6517738, -0.0263837, -0.03631354, -0.01644966, -0.05851095, -0.0849794, -0.01611732, + -0.02618679, 0.05579391, -0.02618679, -0.01611732, -0.0849794, -0.05851095, -0.01644966, -0.03631354, + -0.0263837}; - double[] expected_im = { - 0, -0.04125649, -0.07121312, 0.02554502, - 0.00774181, -0.08723921, -0.02314382, -0.02021455, - 0, 0.02021455, 0.02314382, 0.08723921, - -0.00774181, -0.02554502, 0.07121312, 0.04125649 + double[] expected_im = {0, -0.04125649, -0.07121312, 0.02554502, 0.00774181, -0.08723921, -0.02314382, + -0.02021455, 0, 0.02021455, 0.02314382, 0.08723921, -0.00774181, -0.02554502, 0.07121312, 0.04125649 }; @@ -257,81 +239,64 @@ public void test_ifft_with_real_numpy_data() { @Test public void test_fft_two_dim_8_times_8() { - MatrixBlock re = new MatrixBlock(8, 8, new double[]{ - 0.8435874964408077, 0.3565209485970835, 0.6221038572251737, 0.05712418097055716, - 0.9301368966310067, 0.7748052735242277, 0.21117129518682443, 0.08407931152930459, - 0.5861235649815163, 0.45860122035396356, 0.6647476180103304, 0.9167930424492593, - 0.6310726270028377, 0.11110504251770592, 0.32369996452324756, 0.5790548902504138, - 0.5712310851880162, 0.5967356161025353, 0.6441861776319489, 0.14402445187596158, - 0.22642623625293545, 0.922443731897705, 0.9527667119829785, 0.2250880965427453, - 0.5755375055168817, 0.48898427237526954, 0.24518238824389693, 0.832292384016089, - 0.23789083930394805, 0.5558982102157535, 0.7220016080026206, 0.9666747522359772, - 0.20509423975210916, 0.23170117015755587, 0.7141206714718693, 0.2014158450611332, - 0.6486924358372994, 0.9044990419216931, 0.19849364935627056, 0.23340297110822106, - 0.46854050631969246, 0.10134155509558795, 0.5563200388698989, 0.2669820016661475, - 0.8889445005077763, 0.4273462470993935, 0.8269490075576963, 0.044351336481537995, - 0.3771564738915597, 0.11333723996854606, 0.6913138435759023, 0.062431275099310124, - 0.8003013976959878, 0.1276686539064662, 0.975167392001707, 0.44595301043682656, - 0.18401328301977316, 0.7158585484384759, 0.3240126702723025, 0.740836665073052, - 0.8890279623888511, 0.8841266040978419, 0.3058930798936259, 0.8987579873722049 - }); - - MatrixBlock im = new MatrixBlock(1, 16, new double[]{ - 0.8572457113722648, 0.668182795310341, 0.9739416721141464, 0.8189153345383146, - 0.6425950286263254, 0.3569634253534639, 0.19715070300424575, 0.8915344479242211, - 0.39207930659031054, 0.1625193685179268, 0.2523438052868171, 0.30940628850519547, - 0.7461468672112159, 0.7123766750132684, 0.5261041429273977, 0.867155304805022, - 0.7207769261821749, 0.9139070611733158, 0.7638265842242135, 0.3508092733308539, - 0.6075639148195967, 0.9615531048215422, 0.719499617407839, 0.9616615941848492, - 0.2667126256574347, 0.8215093145949468, 0.4240476512138287, 0.5015798652459079, - 0.19784651066995873, 0.42315603332105356, 0.5575575283922164, 0.9051304828282485, - 0.30117855478511435, 0.14219967492505514, 0.32675429179906557, 0.04889894374947912, - 0.8338579676700041, 0.370201089804747, 0.06025987717830994, 0.9407970353033787, - 0.9871788482561391, 0.75984297199074, 0.414969247979073, 0.2453785474698862, - 0.06295683447294731, 0.40141192931768566, 0.19520663793867488, 0.3179027928938928, - 0.591138083168947, 0.5318366162549014, 0.04865894304644136, 0.5339043989658795, - 0.09892519435896363, 0.31616794516128466, 0.06702286400447643, 0.8466767273121609, - 0.8134875055724791, 0.6232554321597641, 0.21208039111457444, 0.25629831822305926, - 0.7373140896724466, 0.020486629088602437, 0.8666668269441752, 0.20094387974200512 - }); - - double[] expected_re = { - 32.51214260297584, -3.4732779237490314, -0.7257760912890102, -1.9627494786611792, - 3.571671446098747, 1.0451692206901078, 0.8970702451384204, -1.3739767803210428, - 4.892442103095981, -1.1656855109832338, -0.5854908742291178, -1.3497699084098418, - -1.377003693155216, -2.1698030461214923, 0.8172129683973663, -0.9259076518379679, - -1.1343756245445045, -1.8734967800709579, 1.7367517585478862, 0.07349671655414491, - -1.5933768052439223, 2.7965196291943983, 4.292588673604611, -1.1032899622026413, - -2.4643093702874834, 2.109128987930992, 3.2834030498896456, 0.21371926254596152, - -0.3107488550365316, 0.7293395030253796, -2.542403789759091, -1.8570654162590052, - -2.325781245331303, 0.7963395911053484, -2.351990667205867, -2.4241188304485735, - 4.689766636746301, 3.4748121457306116, 0.5539071663846459, -0.950099313504134, - 2.122310975524349, 4.527637759721644, -2.125596093625001, 1.7676565539001592, - 5.748332643019926, 0.860140632830907, 2.9735142186218484, -1.7198774815194848, - -0.18418859401548549, 1.1909629561342188, 0.21710627714418418, -1.5537184277268996, - -0.5486540814747869, 0.14807346060743987, 2.4333154010438087, -3.2930077637380393, - -2.3820067665775113, 2.5463581304688057, 2.5927580716559615, 1.8921802492721915, - 0.4957713559465988, -0.4983536537999108, 3.5808175362367676, 0.7530823235547575 - }; - - double[] expected_im = { - 32.64565805549281, 5.177639365468945, 1.1792020104097647, -0.4850627423320939, - -1.719468548169175, -3.064146170894837, 3.3226243586118906, 2.3819341640916107, - 1.5824429804361388, -2.192940882164737, 0.5774407122593543, 0.16873948200103983, - 4.297014293326352, -3.1712082122026883, 0.9741291131305898, -2.4929883795121235, - 1.111763301820595, -1.4012254390671657, -0.33687898317382636, 2.324190267133635, - -2.8862969254091397, -4.7558982401265135, 1.8244587481290004, 0.5310550630270396, - 2.655726742689745, 2.510014260306531, 0.25589537824783704, 1.8720307201415736, - -2.6458046644482884, 2.1732611302115585, -2.5162250969793227, -0.9103444457919911, - 2.2835527482590248, 0.5187392677625127, -3.335253420903965, 1.4668560670097441, - -1.9681585205341436, -2.81914578771063, 4.818094364700921, -0.877636803126361, - 1.803174743823159, 3.1346192487664277, -3.564058675191744, -0.3391381913837902, - -1.2897867384105863, 2.315065426377637, 1.5764817121472765, 2.412894091248795, - -2.3182678917385218, -3.057303547366563, 0.033996764974414395, -0.5825423640666696, - 6.395088232674363, -0.5553659624089358, 1.1079219041153268, 0.1094531803830765, - 3.488182265163636, -1.5698242466218544, -0.1013387045518459, 0.9269290699615746, - -0.699890233104248, 3.617209720991753, -0.5565163478425035, 3.502962737763559 - }; + MatrixBlock re = new MatrixBlock(8, 8, + new double[] {0.8435874964408077, 0.3565209485970835, 0.6221038572251737, 0.05712418097055716, + 0.9301368966310067, 0.7748052735242277, 0.21117129518682443, 0.08407931152930459, 0.5861235649815163, + 0.45860122035396356, 0.6647476180103304, 0.9167930424492593, 0.6310726270028377, 0.11110504251770592, + 0.32369996452324756, 0.5790548902504138, 0.5712310851880162, 0.5967356161025353, 0.6441861776319489, + 0.14402445187596158, 0.22642623625293545, 0.922443731897705, 0.9527667119829785, 0.2250880965427453, + 0.5755375055168817, 0.48898427237526954, 0.24518238824389693, 0.832292384016089, 0.23789083930394805, + 0.5558982102157535, 0.7220016080026206, 0.9666747522359772, 0.20509423975210916, 0.23170117015755587, + 0.7141206714718693, 0.2014158450611332, 0.6486924358372994, 0.9044990419216931, 0.19849364935627056, + 0.23340297110822106, 0.46854050631969246, 0.10134155509558795, 0.5563200388698989, 0.2669820016661475, + 0.8889445005077763, 0.4273462470993935, 0.8269490075576963, 0.044351336481537995, 0.3771564738915597, + 0.11333723996854606, 0.6913138435759023, 0.062431275099310124, 0.8003013976959878, 0.1276686539064662, + 0.975167392001707, 0.44595301043682656, 0.18401328301977316, 0.7158585484384759, 0.3240126702723025, + 0.740836665073052, 0.8890279623888511, 0.8841266040978419, 0.3058930798936259, 0.8987579873722049}); + + MatrixBlock im = new MatrixBlock(1, 16, + new double[] {0.8572457113722648, 0.668182795310341, 0.9739416721141464, 0.8189153345383146, + 0.6425950286263254, 0.3569634253534639, 0.19715070300424575, 0.8915344479242211, 0.39207930659031054, + 0.1625193685179268, 0.2523438052868171, 0.30940628850519547, 0.7461468672112159, 0.7123766750132684, + 0.5261041429273977, 0.867155304805022, 0.7207769261821749, 0.9139070611733158, 0.7638265842242135, + 0.3508092733308539, 0.6075639148195967, 0.9615531048215422, 0.719499617407839, 0.9616615941848492, + 0.2667126256574347, 0.8215093145949468, 0.4240476512138287, 0.5015798652459079, 0.19784651066995873, + 0.42315603332105356, 0.5575575283922164, 0.9051304828282485, 0.30117855478511435, 0.14219967492505514, + 0.32675429179906557, 0.04889894374947912, 0.8338579676700041, 0.370201089804747, 0.06025987717830994, + 0.9407970353033787, 0.9871788482561391, 0.75984297199074, 0.414969247979073, 0.2453785474698862, + 0.06295683447294731, 0.40141192931768566, 0.19520663793867488, 0.3179027928938928, 0.591138083168947, + 0.5318366162549014, 0.04865894304644136, 0.5339043989658795, 0.09892519435896363, 0.31616794516128466, + 0.06702286400447643, 0.8466767273121609, 0.8134875055724791, 0.6232554321597641, 0.21208039111457444, + 0.25629831822305926, 0.7373140896724466, 0.020486629088602437, 0.8666668269441752, + 0.20094387974200512}); + + double[] expected_re = {32.51214260297584, -3.4732779237490314, -0.7257760912890102, -1.9627494786611792, + 3.571671446098747, 1.0451692206901078, 0.8970702451384204, -1.3739767803210428, 4.892442103095981, + -1.1656855109832338, -0.5854908742291178, -1.3497699084098418, -1.377003693155216, -2.1698030461214923, + 0.8172129683973663, -0.9259076518379679, -1.1343756245445045, -1.8734967800709579, 1.7367517585478862, + 0.07349671655414491, -1.5933768052439223, 2.7965196291943983, 4.292588673604611, -1.1032899622026413, + -2.4643093702874834, 2.109128987930992, 3.2834030498896456, 0.21371926254596152, -0.3107488550365316, + 0.7293395030253796, -2.542403789759091, -1.8570654162590052, -2.325781245331303, 0.7963395911053484, + -2.351990667205867, -2.4241188304485735, 4.689766636746301, 3.4748121457306116, 0.5539071663846459, + -0.950099313504134, 2.122310975524349, 4.527637759721644, -2.125596093625001, 1.7676565539001592, + 5.748332643019926, 0.860140632830907, 2.9735142186218484, -1.7198774815194848, -0.18418859401548549, + 1.1909629561342188, 0.21710627714418418, -1.5537184277268996, -0.5486540814747869, 0.14807346060743987, + 2.4333154010438087, -3.2930077637380393, -2.3820067665775113, 2.5463581304688057, 2.5927580716559615, + 1.8921802492721915, 0.4957713559465988, -0.4983536537999108, 3.5808175362367676, 0.7530823235547575}; + + double[] expected_im = {32.64565805549281, 5.177639365468945, 1.1792020104097647, -0.4850627423320939, + -1.719468548169175, -3.064146170894837, 3.3226243586118906, 2.3819341640916107, 1.5824429804361388, + -2.192940882164737, 0.5774407122593543, 0.16873948200103983, 4.297014293326352, -3.1712082122026883, + 0.9741291131305898, -2.4929883795121235, 1.111763301820595, -1.4012254390671657, -0.33687898317382636, + 2.324190267133635, -2.8862969254091397, -4.7558982401265135, 1.8244587481290004, 0.5310550630270396, + 2.655726742689745, 2.510014260306531, 0.25589537824783704, 1.8720307201415736, -2.6458046644482884, + 2.1732611302115585, -2.5162250969793227, -0.9103444457919911, 2.2835527482590248, 0.5187392677625127, + -3.335253420903965, 1.4668560670097441, -1.9681585205341436, -2.81914578771063, 4.818094364700921, + -0.877636803126361, 1.803174743823159, 3.1346192487664277, -3.564058675191744, -0.3391381913837902, + -1.2897867384105863, 2.315065426377637, 1.5764817121472765, 2.412894091248795, -2.3182678917385218, + -3.057303547366563, 0.033996764974414395, -0.5825423640666696, 6.395088232674363, -0.5553659624089358, + 1.1079219041153268, 0.1094531803830765, 3.488182265163636, -1.5698242466218544, -0.1013387045518459, + 0.9269290699615746, -0.699890233104248, 3.617209720991753, -0.5565163478425035, 3.502962737763559}; MatrixBlock[] res = fft(re, im); From 18462b5323cff09d93985ab1a5919f766e3ca093 Mon Sep 17 00:00:00 2001 From: mufwan Date: Thu, 15 Feb 2024 22:12:56 +0100 Subject: [PATCH 063/133] fft_linearized and ifft_linearized dml integration --- .../org/apache/sysds/common/Builtins.java | 2 + .../org/apache/sysds/hops/FunctionOp.java | 18 ++ .../parser/BuiltinFunctionExpression.java | 174 +++++++++++++++--- .../apache/sysds/parser/DMLTranslator.java | 2 + .../instructions/CPInstructionParser.java | 2 + .../cp/MultiReturnBuiltinCPInstruction.java | 29 ++- ...turnComplexMatrixBuiltinCPInstruction.java | 15 ++ .../runtime/matrix/data/LibCommonsMath.java | 101 ++++++++-- .../runtime/matrix/data/LibMatrixFourier.java | 4 +- .../test/component/matrix/FourierTest.java | 35 ++++ 10 files changed, 336 insertions(+), 46 deletions(-) diff --git a/src/main/java/org/apache/sysds/common/Builtins.java b/src/main/java/org/apache/sysds/common/Builtins.java index 9b18d68be3b..e331e741c3c 100644 --- a/src/main/java/org/apache/sysds/common/Builtins.java +++ b/src/main/java/org/apache/sysds/common/Builtins.java @@ -134,6 +134,7 @@ public enum Builtins { FIX_INVALID_LENGTHS("fixInvalidLengths", true), FIX_INVALID_LENGTHS_APPLY("fixInvalidLengthsApply", true), FFT("fft", false, ReturnType.MULTI_RETURN), + FFT_LINEARIZED("fft_linearized", false, ReturnType.MULTI_RETURN), FF_TRAIN("ffTrain", true), FF_PREDICT("ffPredict", true), FLOOR("floor", false), @@ -156,6 +157,7 @@ public enum Builtins { HYPERBAND("hyperband", true), IFELSE("ifelse", false), IFFT("ifft", false, ReturnType.MULTI_RETURN), + IFFT_LINEARIZED("ifft_linearized", false, ReturnType.MULTI_RETURN), IMG_MIRROR("img_mirror", true), IMG_MIRROR_LINEARIZED("img_mirror_linearized", true), IMG_BRIGHTNESS("img_brightness", true), diff --git a/src/main/java/org/apache/sysds/hops/FunctionOp.java b/src/main/java/org/apache/sysds/hops/FunctionOp.java index 4d439bf1001..217b0554571 100644 --- a/src/main/java/org/apache/sysds/hops/FunctionOp.java +++ b/src/main/java/org/apache/sysds/hops/FunctionOp.java @@ -211,6 +211,16 @@ else if ( getFunctionName().equalsIgnoreCase("ifft") ) { long outputIm = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(1).getDim1(), getOutputs().get(1).getDim2(), 1.0); return outputRe+outputIm; } + else if ( getFunctionName().equalsIgnoreCase("fft_linearized") ) { + long outputRe = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(0).getDim1(), getOutputs().get(0).getDim2(), 1.0); + long outputIm = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(1).getDim1(), getOutputs().get(1).getDim2(), 1.0); + return outputRe+outputIm; + } + else if ( getFunctionName().equalsIgnoreCase("ifft_linearized") ) { + long outputRe = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(0).getDim1(), getOutputs().get(0).getDim2(), 1.0); + long outputIm = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(1).getDim1(), getOutputs().get(1).getDim2(), 1.0); + return outputRe+outputIm; + } else if ( getFunctionName().equalsIgnoreCase("lstm") || getFunctionName().equalsIgnoreCase("lstm_backward") ) { // TODO: To allow for initial version to always run on the GPU return 0; @@ -268,6 +278,14 @@ else if ( getFunctionName().equalsIgnoreCase("ifft") ) { // 2 matrices of size same as the input return 2*OptimizerUtils.estimateSizeExactSparsity(getInput().get(0).getDim1(), getInput().get(0).getDim2(), 1.0); } + else if ( getFunctionName().equalsIgnoreCase("fft_linearized") ) { + // 2 matrices of size same as the input + return 2*OptimizerUtils.estimateSizeExactSparsity(getInput().get(0).getDim1(), getInput().get(0).getDim2(), 1.0); + } + else if ( getFunctionName().equalsIgnoreCase("ifft_linearized") ) { + // 2 matrices of size same as the input + return 2*OptimizerUtils.estimateSizeExactSparsity(getInput().get(0).getDim1(), getInput().get(0).getDim2(), 1.0); + } else if (getFunctionName().equalsIgnoreCase("batch_norm2d") || getFunctionName().equalsIgnoreCase("batch_norm2d_backward") || getFunctionName().equalsIgnoreCase("batch_norm2d_train") || getFunctionName().equalsIgnoreCase("batch_norm2d_test")) { return 0; diff --git a/src/main/java/org/apache/sysds/parser/BuiltinFunctionExpression.java b/src/main/java/org/apache/sysds/parser/BuiltinFunctionExpression.java index 9a2dc041201..3124c518c16 100644 --- a/src/main/java/org/apache/sysds/parser/BuiltinFunctionExpression.java +++ b/src/main/java/org/apache/sysds/parser/BuiltinFunctionExpression.java @@ -356,28 +356,37 @@ public void validateExpression(MultiAssignmentStatement stmt, HashMap 0) && ((n & (n - 1)) == 0); + } + private static void setDimensions(DataIdentifier out, Expression exp) { out.setDataType(DataType.MATRIX); out.setValueType(ValueType.FP64); diff --git a/src/main/java/org/apache/sysds/parser/DMLTranslator.java b/src/main/java/org/apache/sysds/parser/DMLTranslator.java index 537de1abbaa..47a4be8f71c 100644 --- a/src/main/java/org/apache/sysds/parser/DMLTranslator.java +++ b/src/main/java/org/apache/sysds/parser/DMLTranslator.java @@ -2239,6 +2239,8 @@ private Hop processMultipleReturnBuiltinFunctionExpression(BuiltinFunctionExpres case EIGEN: case FFT: case IFFT: + case FFT_LINEARIZED: + case IFFT_LINEARIZED: case LSTM: case LSTM_BACKWARD: case BATCH_NORM2D: diff --git a/src/main/java/org/apache/sysds/runtime/instructions/CPInstructionParser.java b/src/main/java/org/apache/sysds/runtime/instructions/CPInstructionParser.java index 37540da8da1..81554bae04c 100644 --- a/src/main/java/org/apache/sysds/runtime/instructions/CPInstructionParser.java +++ b/src/main/java/org/apache/sysds/runtime/instructions/CPInstructionParser.java @@ -334,6 +334,8 @@ public class CPInstructionParser extends InstructionParser { String2CPInstructionType.put( "eigen", CPType.MultiReturnBuiltin); String2CPInstructionType.put("fft", CPType.MultiReturnBuiltin); String2CPInstructionType.put("ifft", CPType.MultiReturnComplexMatrixBuiltin); + String2CPInstructionType.put("fft_linearized", CPType.MultiReturnBuiltin); + String2CPInstructionType.put("ifft_linearized", CPType.MultiReturnComplexMatrixBuiltin); String2CPInstructionType.put( "svd", CPType.MultiReturnBuiltin); String2CPInstructionType.put( "partition", CPType.Partition); diff --git a/src/main/java/org/apache/sysds/runtime/instructions/cp/MultiReturnBuiltinCPInstruction.java b/src/main/java/org/apache/sysds/runtime/instructions/cp/MultiReturnBuiltinCPInstruction.java index 1150f037e8b..4719948ec01 100644 --- a/src/main/java/org/apache/sysds/runtime/instructions/cp/MultiReturnBuiltinCPInstruction.java +++ b/src/main/java/org/apache/sysds/runtime/instructions/cp/MultiReturnBuiltinCPInstruction.java @@ -90,7 +90,7 @@ else if ( opcode.equalsIgnoreCase("eigen") ) { return new MultiReturnBuiltinCPInstruction(null, in1, outputs, opcode, str); } - else if (opcode.equalsIgnoreCase("fft")) { + else if(parts.length == 4 && opcode.equalsIgnoreCase("fft")) { // one input and two outputs CPOperand in1 = new CPOperand(parts[1]); outputs.add(new CPOperand(parts[2], ValueType.FP64, DataType.MATRIX)); @@ -98,7 +98,32 @@ else if (opcode.equalsIgnoreCase("fft")) { return new MultiReturnBuiltinCPInstruction(null, in1, outputs, opcode, str); - } + } + else if(parts.length == 3 && opcode.equalsIgnoreCase("fft")) { + // one input and two outputs + outputs.add(new CPOperand(parts[1], ValueType.FP64, DataType.MATRIX)); + outputs.add(new CPOperand(parts[2], ValueType.FP64, DataType.MATRIX)); + + return new MultiReturnBuiltinCPInstruction(null, null, outputs, opcode, str); + + } + else if(parts.length == 4 && opcode.equalsIgnoreCase("fft_linearized")) { + // one input and two outputs + CPOperand in1 = new CPOperand(parts[1]); + outputs.add(new CPOperand(parts[2], ValueType.FP64, DataType.MATRIX)); + outputs.add(new CPOperand(parts[3], ValueType.FP64, DataType.MATRIX)); + + return new MultiReturnBuiltinCPInstruction(null, in1, outputs, opcode, str); + + } + else if(parts.length == 3 && opcode.equalsIgnoreCase("fft_linearized")) { + // one input and two outputs + outputs.add(new CPOperand(parts[1], ValueType.FP64, DataType.MATRIX)); + outputs.add(new CPOperand(parts[2], ValueType.FP64, DataType.MATRIX)); + + return new MultiReturnBuiltinCPInstruction(null, null, outputs, opcode, str); + + } else if ( opcode.equalsIgnoreCase("svd") ) { CPOperand in1 = new CPOperand(parts[1]); diff --git a/src/main/java/org/apache/sysds/runtime/instructions/cp/MultiReturnComplexMatrixBuiltinCPInstruction.java b/src/main/java/org/apache/sysds/runtime/instructions/cp/MultiReturnComplexMatrixBuiltinCPInstruction.java index ae045dbc3e5..91d0d4d8e18 100644 --- a/src/main/java/org/apache/sysds/runtime/instructions/cp/MultiReturnComplexMatrixBuiltinCPInstruction.java +++ b/src/main/java/org/apache/sysds/runtime/instructions/cp/MultiReturnComplexMatrixBuiltinCPInstruction.java @@ -84,6 +84,21 @@ public static MultiReturnComplexMatrixBuiltinCPInstruction parseInstruction(Stri outputs.add(new CPOperand(parts[2], ValueType.FP64, DataType.MATRIX)); outputs.add(new CPOperand(parts[3], ValueType.FP64, DataType.MATRIX)); + return new MultiReturnComplexMatrixBuiltinCPInstruction(null, in1, outputs, opcode, str); + } else if (parts.length == 5 && opcode.equalsIgnoreCase("ifft_linearized")) { + // one input and two outputs + CPOperand in1 = new CPOperand(parts[1]); + CPOperand in2 = new CPOperand(parts[2]); + outputs.add(new CPOperand(parts[3], ValueType.FP64, DataType.MATRIX)); + outputs.add(new CPOperand(parts[4], ValueType.FP64, DataType.MATRIX)); + + return new MultiReturnComplexMatrixBuiltinCPInstruction(null, in1, in2, outputs, opcode, str); + } else if (parts.length == 4 && opcode.equalsIgnoreCase("ifft_linearized")) { + // one input and two outputs + CPOperand in1 = new CPOperand(parts[1]); + outputs.add(new CPOperand(parts[2], ValueType.FP64, DataType.MATRIX)); + outputs.add(new CPOperand(parts[3], ValueType.FP64, DataType.MATRIX)); + return new MultiReturnComplexMatrixBuiltinCPInstruction(null, in1, outputs, opcode, str); } diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java index 729c90c922e..ec170d668a4 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java @@ -51,6 +51,8 @@ import static org.apache.sysds.runtime.matrix.data.LibMatrixFourier.fft; import static org.apache.sysds.runtime.matrix.data.LibMatrixFourier.ifft; +import static org.apache.sysds.runtime.matrix.data.LibMatrixFourier.fft_linearized; +import static org.apache.sysds.runtime.matrix.data.LibMatrixFourier.ifft_linearized; /** * Library for matrix operations that need invocation of @@ -81,6 +83,8 @@ public static boolean isSupportedMultiReturnOperation( String opcode ) { case "eigen": case "fft": case "ifft": + case "fft_linearized": + case "ifft_linearized": case "svd": return true; default: return false; } @@ -134,6 +138,10 @@ else if (opcode.equals("fft")) return computeFFT(in); else if (opcode.equals("ifft")) return computeIFFT(in, null); + else if (opcode.equals("fft_linearized")) + return computeFFT_LINEARIZED(in); + else if (opcode.equals("ifft_linearized")) + return computeIFFT_LINEARIZED(in, null); return null; } @@ -143,6 +151,8 @@ public static MatrixBlock[] multiReturnOperations(MatrixBlock in1, MatrixBlock i switch (opcode) { case "ifft": return computeIFFT(in1, in2); + case "ifft_linearized": + return computeIFFT_LINEARIZED(in1, in2); default: return null; } @@ -288,38 +298,99 @@ private static EigenDecomposition computeEigenRegularized(MatrixBlock in) { /** * Function to perform FFT on a given matrix. * - * @param in matrix object + * @param re matrix object * @return array of matrix blocks */ - private static MatrixBlock[] computeFFT(MatrixBlock in) { - if (in == null || in.isEmptyBlock(false)) + private static MatrixBlock[] computeFFT(MatrixBlock re) { + if(re == null) throw new DMLRuntimeException("Invalid empty block"); - + if (re.isEmptyBlock(false)) { + // Return the original matrix as the result + return new MatrixBlock[]{re, new MatrixBlock(re.getNumRows(), re.getNumColumns(), true)}; // Assuming you need to return two matrices: the real part and an imaginary part initialized to 0. + } // run fft - in.sparseToDense(); - return fft(in); + re.sparseToDense(); + return fft(re); + } + + private static boolean isMatrixAllZeros(MatrixBlock matrix) { + // Fast check for sparse representation + if (matrix.isInSparseFormat()) { + return matrix.getNonZeros() == 0; + } + // Dense format check + double sum = matrix.sum(); + return sum == 0; } /** * Function to perform IFFT on a given matrix. * - * @param in matrix object + * @param re matrix object + * @param im matrix object * @return array of matrix blocks */ - private static MatrixBlock[] computeIFFT(MatrixBlock in1, MatrixBlock in2) { - if (in1 == null || in1.isEmptyBlock(false)) + private static MatrixBlock[] computeIFFT(MatrixBlock re, MatrixBlock im) { + if (re == null) throw new DMLRuntimeException("Invalid empty block"); // run ifft - if (in2 != null) { - in1.sparseToDense(); - in2.sparseToDense(); - return ifft(in1, in2); + if (im != null && !im.isEmptyBlock(false)) { + re.sparseToDense(); + im.sparseToDense(); + return ifft(re, im); } else { - in1.sparseToDense(); - return ifft(in1); + if (re.isEmptyBlock(false)) { + // Return the original matrix as the result + return new MatrixBlock[]{re, new MatrixBlock(re.getNumRows(), re.getNumColumns(), true)}; // Assuming you need to return two matrices: the real part and an imaginary part initialized to 0. + } + re.sparseToDense(); + return ifft(re); + } + } + + /** + * Function to perform FFT_LINEARIZED on a given matrix. + * + * @param re matrix object + * @return array of matrix blocks + */ + private static MatrixBlock[] computeFFT_LINEARIZED(MatrixBlock re) { + if(re == null) + throw new DMLRuntimeException("Invalid empty block"); + if (re.isEmptyBlock(false)) { + // Return the original matrix as the result + return new MatrixBlock[]{re, new MatrixBlock(re.getNumRows(), re.getNumColumns(), true)}; // Assuming you need to return two matrices: the real part and an imaginary part initialized to 0. } + // run fft + re.sparseToDense(); + return fft_linearized(re); + } + + /** + * Function to perform IFFT_LINEARIZED on a given matrix. + * + * @param re matrix object + * @param im matrix object + * @return array of matrix blocks + */ + private static MatrixBlock[] computeIFFT_LINEARIZED(MatrixBlock re, MatrixBlock im) { + if (re == null) + throw new DMLRuntimeException("Invalid empty block"); + // run ifft + if (im != null && !im.isEmptyBlock(false)) { + re.sparseToDense(); + im.sparseToDense(); + return ifft_linearized(re, im); + } else { + if (re.isEmptyBlock(false)) { + // Return the original matrix as the result + return new MatrixBlock[]{re, new MatrixBlock(re.getNumRows(), re.getNumColumns(), true)}; // Assuming you need to return two matrices: the real part and an imaginary part initialized to 0. + } + re.sparseToDense(); + return ifft_linearized(re); + } } /** diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java index 4c0c10c7021..90100e3f68c 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java @@ -76,7 +76,7 @@ public static MatrixBlock[] fft_linearized(MatrixBlock re, MatrixBlock im) { int rows = re.getNumRows(); int cols = re.getNumColumns(); - if(!isPowerOfTwo(rows) || !isPowerOfTwo(cols)) + if(!isPowerOfTwo(cols)) throw new RuntimeException("false dimensions"); fft(re.getDenseBlockValues(), im.getDenseBlockValues(), rows, cols, false); @@ -97,7 +97,7 @@ public static MatrixBlock[] ifft_linearized(MatrixBlock re, MatrixBlock im) { int rows = re.getNumRows(); int cols = re.getNumColumns(); - if(!isPowerOfTwo(rows) || !isPowerOfTwo(cols)) + if(!isPowerOfTwo(cols)) throw new RuntimeException("false dimensions"); ifft(re.getDenseBlockValues(), im.getDenseBlockValues(), rows, cols, false); diff --git a/src/test/java/org/apache/sysds/test/component/matrix/FourierTest.java b/src/test/java/org/apache/sysds/test/component/matrix/FourierTest.java index ea279ef0bcf..caa9d07d4a1 100644 --- a/src/test/java/org/apache/sysds/test/component/matrix/FourierTest.java +++ b/src/test/java/org/apache/sysds/test/component/matrix/FourierTest.java @@ -24,6 +24,8 @@ import static org.apache.sysds.runtime.matrix.data.LibMatrixFourier.fft; import static org.apache.sysds.runtime.matrix.data.LibMatrixFourier.ifft; +import static org.apache.sysds.runtime.matrix.data.LibMatrixFourier.fft_linearized; +import static org.apache.sysds.runtime.matrix.data.LibMatrixFourier.ifft_linearized; import static org.junit.Assert.assertArrayEquals; @@ -305,4 +307,37 @@ public void test_fft_two_dim_8_times_8() { } + @Test + public void test_fft_linearized() { + + MatrixBlock re = new MatrixBlock(2, 4, new double[] {0, 18, -15, 3, 0, 18, -15, 3}); + MatrixBlock im = new MatrixBlock(1, 4, new double[8]); + + double[] expected_re = {6, 15, -36, 15, 6, 15, -36, 15}; + double[] expected_im = {0, -15, 0, 15, 0, -15, 0, 15}; + + fft_linearized(re, im); + + assertArrayEquals(expected_re, re.getDenseBlockValues(), 0.0001); + assertArrayEquals(expected_im, im.getDenseBlockValues(), 0.0001); + + } + + @Test + public void test_ifft_linearized() { + + double[] in_re = new double[] {1, -2, 3, -4, 1, -2, 3, -4}; + double[] in_im = new double[] {0, 0, 0, 0, 0, 0, 0, 0}; + + MatrixBlock re = new MatrixBlock(2, 4, in_re); + MatrixBlock im = new MatrixBlock(2, 4, in_im); + + MatrixBlock[] inter = fft_linearized(re, im); + MatrixBlock[] res = ifft_linearized(inter[0], inter[1]); + + assertArrayEquals(in_re, res[0].getDenseBlockValues(), 0.0001); + assertArrayEquals(in_im, res[1].getDenseBlockValues(), 0.0001); + + } + } From bf99d6b1b85511595d55d457d58dcc8583fe3a39 Mon Sep 17 00:00:00 2001 From: Frederic Zoepffel Date: Sat, 17 Feb 2024 12:18:27 +0100 Subject: [PATCH 064/133] purposeless bracket adjusted --- .../cp/MultiReturnComplexMatrixBuiltinCPInstruction.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/java/org/apache/sysds/runtime/instructions/cp/MultiReturnComplexMatrixBuiltinCPInstruction.java b/src/main/java/org/apache/sysds/runtime/instructions/cp/MultiReturnComplexMatrixBuiltinCPInstruction.java index 91d0d4d8e18..a279ff56654 100644 --- a/src/main/java/org/apache/sysds/runtime/instructions/cp/MultiReturnComplexMatrixBuiltinCPInstruction.java +++ b/src/main/java/org/apache/sysds/runtime/instructions/cp/MultiReturnComplexMatrixBuiltinCPInstruction.java @@ -101,8 +101,7 @@ public static MultiReturnComplexMatrixBuiltinCPInstruction parseInstruction(Stri return new MultiReturnComplexMatrixBuiltinCPInstruction(null, in1, outputs, opcode, str); } - - { + else { throw new DMLRuntimeException("Invalid opcode in MultiReturnBuiltin instruction: " + opcode); } From 8d514df226bc9df3d1c84d8adb6de2b65ad67bee Mon Sep 17 00:00:00 2001 From: Frederic Zoepffel Date: Sat, 17 Feb 2024 12:38:39 +0100 Subject: [PATCH 065/133] added overloads for computeIFFT and computeIFFT_LINEARIZED, removed isMatrixAllZeros --- .../runtime/matrix/data/LibCommonsMath.java | 35 ++++++++++++------- 1 file changed, 23 insertions(+), 12 deletions(-) diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java index ec170d668a4..6de24d6900a 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java @@ -137,11 +137,11 @@ else if (opcode.equals("svd")) else if (opcode.equals("fft")) return computeFFT(in); else if (opcode.equals("ifft")) - return computeIFFT(in, null); + return computeIFFT(in); else if (opcode.equals("fft_linearized")) return computeFFT_LINEARIZED(in); else if (opcode.equals("ifft_linearized")) - return computeIFFT_LINEARIZED(in, null); + return computeIFFT_LINEARIZED(in); return null; } @@ -313,16 +313,6 @@ private static MatrixBlock[] computeFFT(MatrixBlock re) { return fft(re); } - private static boolean isMatrixAllZeros(MatrixBlock matrix) { - // Fast check for sparse representation - if (matrix.isInSparseFormat()) { - return matrix.getNonZeros() == 0; - } - // Dense format check - double sum = matrix.sum(); - return sum == 0; - } - /** * Function to perform IFFT on a given matrix. * @@ -349,6 +339,17 @@ private static MatrixBlock[] computeIFFT(MatrixBlock re, MatrixBlock im) { } } + + /** + * Function to perform IFFT on a given matrix. + * + * @param re matrix object + * @return array of matrix blocks + */ + private static MatrixBlock[] computeIFFT(MatrixBlock re) { + return computeIFFT(re, null); + } + /** * Function to perform FFT_LINEARIZED on a given matrix. * @@ -392,6 +393,16 @@ private static MatrixBlock[] computeIFFT_LINEARIZED(MatrixBlock re, MatrixBlock return ifft_linearized(re); } } + + /** + * Function to perform IFFT_LINEARIZED on a given matrix + * + * @param re matrix object + * @return array of matrix blocks + */ + computeIFFT_LINEARIZED(MatrixBlock re){ + return computeIFFT_LINEARIZED(re, null); + } /** * Performs Singular Value Decomposition. Calls Apache Commons Math SVD. From 2fefb476a59bdafb61651e5406c4f3312a4540d2 Mon Sep 17 00:00:00 2001 From: mufwan <105200913+mufwan@users.noreply.github.com> Date: Sat, 17 Feb 2024 13:29:31 +0100 Subject: [PATCH 066/133] Update BuiltinFunctionExpression.java Remove changes regarding fft in BuiltinFunctionExpression, see python test pr --- .../parser/BuiltinFunctionExpression.java | 80 +++++++------------ 1 file changed, 27 insertions(+), 53 deletions(-) diff --git a/src/main/java/org/apache/sysds/parser/BuiltinFunctionExpression.java b/src/main/java/org/apache/sysds/parser/BuiltinFunctionExpression.java index 3124c518c16..6246e67afd3 100644 --- a/src/main/java/org/apache/sysds/parser/BuiltinFunctionExpression.java +++ b/src/main/java/org/apache/sysds/parser/BuiltinFunctionExpression.java @@ -356,37 +356,28 @@ public void validateExpression(MultiAssignmentStatement stmt, HashMap Date: Sat, 17 Feb 2024 15:09:06 +0100 Subject: [PATCH 067/133] Update LibCommonsMath.java add return for computeifft --- .../org/apache/sysds/runtime/matrix/data/LibCommonsMath.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java index 6de24d6900a..9ce363ca44b 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java @@ -400,7 +400,7 @@ private static MatrixBlock[] computeIFFT_LINEARIZED(MatrixBlock re, MatrixBlock * @param re matrix object * @return array of matrix blocks */ - computeIFFT_LINEARIZED(MatrixBlock re){ + private static MatrixBlock[] computeIFFT_LINEARIZED(MatrixBlock re){ return computeIFFT_LINEARIZED(re, null); } From b86eee8fa5183dc180afa008d731231bfa3fdca0 Mon Sep 17 00:00:00 2001 From: Jessica Priebe Date: Sun, 18 Feb 2024 14:01:53 +0100 Subject: [PATCH 068/133] added parallelization --- .../runtime/matrix/data/LibCommonsMath.java | 46 ++++--- .../runtime/matrix/data/LibMatrixFourier.java | 116 ++++++++++++------ .../test/component/matrix/FourierTest.java | 34 ++--- 3 files changed, 124 insertions(+), 72 deletions(-) diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java index 9ce363ca44b..6e7fa816fd0 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java @@ -135,13 +135,13 @@ else if (opcode.equals("eigen_qr")) else if (opcode.equals("svd")) return computeSvd(in); else if (opcode.equals("fft")) - return computeFFT(in); + return computeFFT(in, threads); else if (opcode.equals("ifft")) - return computeIFFT(in); + return computeIFFT(in, threads); else if (opcode.equals("fft_linearized")) - return computeFFT_LINEARIZED(in); + return computeFFT_LINEARIZED(in, threads); else if (opcode.equals("ifft_linearized")) - return computeIFFT_LINEARIZED(in); + return computeIFFT_LINEARIZED(in, threads); return null; } @@ -150,9 +150,9 @@ public static MatrixBlock[] multiReturnOperations(MatrixBlock in1, MatrixBlock i switch (opcode) { case "ifft": - return computeIFFT(in1, in2); + return computeIFFT(in1, in2, threads); case "ifft_linearized": - return computeIFFT_LINEARIZED(in1, in2); + return computeIFFT_LINEARIZED(in1, in2, threads); default: return null; } @@ -299,9 +299,10 @@ private static EigenDecomposition computeEigenRegularized(MatrixBlock in) { * Function to perform FFT on a given matrix. * * @param re matrix object + * @param threads number of threads * @return array of matrix blocks */ - private static MatrixBlock[] computeFFT(MatrixBlock re) { + private static MatrixBlock[] computeFFT(MatrixBlock re, int threads) { if(re == null) throw new DMLRuntimeException("Invalid empty block"); if (re.isEmptyBlock(false)) { @@ -310,7 +311,7 @@ private static MatrixBlock[] computeFFT(MatrixBlock re) { } // run fft re.sparseToDense(); - return fft(re); + return fft(re, threads); } /** @@ -318,9 +319,10 @@ private static MatrixBlock[] computeFFT(MatrixBlock re) { * * @param re matrix object * @param im matrix object + * @param threads number of threads * @return array of matrix blocks */ - private static MatrixBlock[] computeIFFT(MatrixBlock re, MatrixBlock im) { + private static MatrixBlock[] computeIFFT(MatrixBlock re, MatrixBlock im, int threads) { if (re == null) throw new DMLRuntimeException("Invalid empty block"); @@ -328,14 +330,14 @@ private static MatrixBlock[] computeIFFT(MatrixBlock re, MatrixBlock im) { if (im != null && !im.isEmptyBlock(false)) { re.sparseToDense(); im.sparseToDense(); - return ifft(re, im); + return ifft(re, im, threads); } else { if (re.isEmptyBlock(false)) { // Return the original matrix as the result return new MatrixBlock[]{re, new MatrixBlock(re.getNumRows(), re.getNumColumns(), true)}; // Assuming you need to return two matrices: the real part and an imaginary part initialized to 0. } re.sparseToDense(); - return ifft(re); + return ifft(re, threads); } } @@ -344,19 +346,21 @@ private static MatrixBlock[] computeIFFT(MatrixBlock re, MatrixBlock im) { * Function to perform IFFT on a given matrix. * * @param re matrix object + * @param threads number of threads * @return array of matrix blocks */ - private static MatrixBlock[] computeIFFT(MatrixBlock re) { - return computeIFFT(re, null); + private static MatrixBlock[] computeIFFT(MatrixBlock re, int threads) { + return computeIFFT(re, null, threads); } /** * Function to perform FFT_LINEARIZED on a given matrix. * * @param re matrix object + * @param threads number of threads * @return array of matrix blocks */ - private static MatrixBlock[] computeFFT_LINEARIZED(MatrixBlock re) { + private static MatrixBlock[] computeFFT_LINEARIZED(MatrixBlock re, int threads) { if(re == null) throw new DMLRuntimeException("Invalid empty block"); if (re.isEmptyBlock(false)) { @@ -365,7 +369,7 @@ private static MatrixBlock[] computeFFT_LINEARIZED(MatrixBlock re) { } // run fft re.sparseToDense(); - return fft_linearized(re); + return fft_linearized(re, threads); } /** @@ -373,9 +377,10 @@ private static MatrixBlock[] computeFFT_LINEARIZED(MatrixBlock re) { * * @param re matrix object * @param im matrix object + * @param threads number of threads * @return array of matrix blocks */ - private static MatrixBlock[] computeIFFT_LINEARIZED(MatrixBlock re, MatrixBlock im) { + private static MatrixBlock[] computeIFFT_LINEARIZED(MatrixBlock re, MatrixBlock im, int threads) { if (re == null) throw new DMLRuntimeException("Invalid empty block"); @@ -383,14 +388,14 @@ private static MatrixBlock[] computeIFFT_LINEARIZED(MatrixBlock re, MatrixBlock if (im != null && !im.isEmptyBlock(false)) { re.sparseToDense(); im.sparseToDense(); - return ifft_linearized(re, im); + return ifft_linearized(re, im, threads); } else { if (re.isEmptyBlock(false)) { // Return the original matrix as the result return new MatrixBlock[]{re, new MatrixBlock(re.getNumRows(), re.getNumColumns(), true)}; // Assuming you need to return two matrices: the real part and an imaginary part initialized to 0. } re.sparseToDense(); - return ifft_linearized(re); + return ifft_linearized(re, threads); } } @@ -398,10 +403,11 @@ private static MatrixBlock[] computeIFFT_LINEARIZED(MatrixBlock re, MatrixBlock * Function to perform IFFT_LINEARIZED on a given matrix * * @param re matrix object + * @param threads number of threads * @return array of matrix blocks */ - private static MatrixBlock[] computeIFFT_LINEARIZED(MatrixBlock re){ - return computeIFFT_LINEARIZED(re, null); + private static MatrixBlock[] computeIFFT_LINEARIZED(MatrixBlock re, int threads){ + return computeIFFT_LINEARIZED(re, null, threads); } /** diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java index 90100e3f68c..d25678cde60 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java @@ -21,24 +21,30 @@ import org.apache.commons.math3.util.FastMath; +import org.apache.sysds.runtime.util.CommonThreadPool; + +import java.util.concurrent.ExecutorService; +import java.util.concurrent.TimeUnit; + public class LibMatrixFourier { /** * Function to perform FFT for two given matrices. The first one represents the real values and the second one the * imaginary values. The output also contains one matrix for the real and one for the imaginary values. * - * @param re matrix object representing the real values - * @param im matrix object representing the imaginary values + * @param re matrix object representing the real values + * @param im matrix object representing the imaginary values + * @param threads number of threads * @return array of two matrix blocks */ - public static MatrixBlock[] fft(MatrixBlock re, MatrixBlock im) { + public static MatrixBlock[] fft(MatrixBlock re, MatrixBlock im, int threads) { int rows = re.getNumRows(); int cols = re.getNumColumns(); if(!isPowerOfTwo(rows) || !isPowerOfTwo(cols)) throw new RuntimeException("false dimensions"); - fft(re.getDenseBlockValues(), im.getDenseBlockValues(), rows, cols, true); + fft(re.getDenseBlockValues(), im.getDenseBlockValues(), rows, cols, threads, true); return new MatrixBlock[] {re, im}; } @@ -47,18 +53,19 @@ public static MatrixBlock[] fft(MatrixBlock re, MatrixBlock im) { * Function to perform IFFT for two given matrices. The first one represents the real values and the second one the * imaginary values. The output also contains one matrix for the real and one for the imaginary values. * - * @param re matrix object representing the real values - * @param im matrix object representing the imaginary values + * @param re matrix object representing the real values + * @param im matrix object representing the imaginary values + * @param threads number of threads * @return array of two matrix blocks */ - public static MatrixBlock[] ifft(MatrixBlock re, MatrixBlock im) { + public static MatrixBlock[] ifft(MatrixBlock re, MatrixBlock im, int threads) { int rows = re.getNumRows(); int cols = re.getNumColumns(); if(!isPowerOfTwo(rows) || !isPowerOfTwo(cols)) throw new RuntimeException("false dimensions"); - ifft(re.getDenseBlockValues(), im.getDenseBlockValues(), rows, cols, true); + ifft(re.getDenseBlockValues(), im.getDenseBlockValues(), rows, cols, threads, true); return new MatrixBlock[] {re, im}; } @@ -68,18 +75,19 @@ public static MatrixBlock[] ifft(MatrixBlock re, MatrixBlock im) { * second one the imaginary values. The output also contains one matrix for the real and one for the imaginary * values. * - * @param re matrix object representing the real values - * @param im matrix object representing the imaginary values + * @param re matrix object representing the real values + * @param im matrix object representing the imaginary values + * @param threads number of threads * @return array of two matrix blocks */ - public static MatrixBlock[] fft_linearized(MatrixBlock re, MatrixBlock im) { + public static MatrixBlock[] fft_linearized(MatrixBlock re, MatrixBlock im, int threads) { int rows = re.getNumRows(); int cols = re.getNumColumns(); if(!isPowerOfTwo(cols)) throw new RuntimeException("false dimensions"); - fft(re.getDenseBlockValues(), im.getDenseBlockValues(), rows, cols, false); + fft(re.getDenseBlockValues(), im.getDenseBlockValues(), rows, cols, threads, false); return new MatrixBlock[] {re, im}; } @@ -89,18 +97,19 @@ public static MatrixBlock[] fft_linearized(MatrixBlock re, MatrixBlock im) { * second one the imaginary values. The output also contains one matrix for the real and one for the imaginary * values. * - * @param re matrix object representing the real values - * @param im matrix object representing the imaginary values + * @param re matrix object representing the real values + * @param im matrix object representing the imaginary values + * @param threads number of threads * @return array of two matrix blocks */ - public static MatrixBlock[] ifft_linearized(MatrixBlock re, MatrixBlock im) { + public static MatrixBlock[] ifft_linearized(MatrixBlock re, MatrixBlock im, int threads) { int rows = re.getNumRows(); int cols = re.getNumColumns(); if(!isPowerOfTwo(cols)) throw new RuntimeException("false dimensions"); - ifft(re.getDenseBlockValues(), im.getDenseBlockValues(), rows, cols, false); + ifft(re.getDenseBlockValues(), im.getDenseBlockValues(), rows, cols, threads, false); return new MatrixBlock[] {re, im}; } @@ -113,21 +122,28 @@ public static MatrixBlock[] ifft_linearized(MatrixBlock re, MatrixBlock im) { * @param im array representing the imaginary values * @param rows number of rows * @param cols number of rows + * @param threads number of threads * @param inclColCalc if true, fft is also calculated for each column, otherwise only for each row */ - public static void fft(double[] re, double[] im, int rows, int cols, boolean inclColCalc) { + public static void fft(double[] re, double[] im, int rows, int cols, int threads, boolean inclColCalc) { double[] re_inter = new double[rows * cols]; double[] im_inter = new double[rows * cols]; + ExecutorService pool = CommonThreadPool.get(threads); + for(int i = 0; i < rows; i++) { - fft_one_dim(re, im, re_inter, im_inter, i * cols, (i + 1) * cols, cols, 1); + final int finalI = i; + pool.submit(() -> fft_one_dim(re, im, re_inter, im_inter, finalI * cols, (finalI + 1) * cols, cols, 1)); } + awaitParallelExecution(pool); - if(inclColCalc) { + if(inclColCalc && rows > 1) { for(int j = 0; j < cols; j++) { - fft_one_dim(re, im, re_inter, im_inter, j, j + rows * cols, rows, cols); + final int finalJ = j; + pool.submit(() -> fft_one_dim(re, im, re_inter, im_inter, finalJ, finalJ + rows * cols, rows, cols)); } + awaitParallelExecution(pool); } } @@ -140,22 +156,29 @@ public static void fft(double[] re, double[] im, int rows, int cols, boolean inc * @param im array representing the imaginary values * @param rows number of rows * @param cols number of rows + * @param threads number of threads * @param inclColCalc if true, fft is also calculated for each column, otherwise only for each row */ - public static void ifft(double[] re, double[] im, int rows, int cols, boolean inclColCalc) { + public static void ifft(double[] re, double[] im, int rows, int cols, int threads, boolean inclColCalc) { double[] re_inter = new double[rows * cols]; double[] im_inter = new double[rows * cols]; - if(inclColCalc) { + ExecutorService pool = CommonThreadPool.get(threads); + + if(inclColCalc && rows > 1) { for(int j = 0; j < cols; j++) { - ifft_one_dim(re, im, re_inter, im_inter, j, j + rows * cols, rows, cols); + final int finalJ = j; + pool.submit(() -> ifft_one_dim(re, im, re_inter, im_inter, finalJ, finalJ + rows * cols, rows, cols)); } + awaitParallelExecution(pool); } for(int i = 0; i < rows; i++) { - ifft_one_dim(re, im, re_inter, im_inter, i * cols, (i + 1) * cols, cols, 1); + final int finalI = i; + pool.submit(() -> ifft_one_dim(re, im, re_inter, im_inter, finalI * cols, (finalI + 1) * cols, cols, 1)); } + awaitParallelExecution(pool); } @@ -262,48 +285,69 @@ public static boolean isPowerOfTwo(int n) { * Function to perform FFT for a given matrix. The given matrix only represents real values. The output contains one * matrix for the real and one for the imaginary values. * - * @param re matrix object representing the real values + * @param re matrix object representing the real values + * @param threads number of threads * @return array of two matrix blocks */ - public static MatrixBlock[] fft(MatrixBlock re) { + public static MatrixBlock[] fft(MatrixBlock re, int threads) { return fft(re, - new MatrixBlock(re.getNumRows(), re.getNumColumns(), new double[re.getNumRows() * re.getNumColumns()])); + new MatrixBlock(re.getNumRows(), re.getNumColumns(), new double[re.getNumRows() * re.getNumColumns()]), + threads); } /** * Function to perform IFFT for a given matrix. The given matrix only represents real values. The output contains * one matrix for the real and one for the imaginary values. * - * @param re matrix object representing the real values + * @param re matrix object representing the real values + * @param threads number of threads * @return array of two matrix blocks */ - public static MatrixBlock[] ifft(MatrixBlock re) { + public static MatrixBlock[] ifft(MatrixBlock re, int threads) { return ifft(re, - new MatrixBlock(re.getNumRows(), re.getNumColumns(), new double[re.getNumRows() * re.getNumColumns()])); + new MatrixBlock(re.getNumRows(), re.getNumColumns(), new double[re.getNumRows() * re.getNumColumns()]), + threads); } /** * Function to perform FFT for each row of a given matrix. The given matrix only represents real values. The output * contains one matrix for the real and one for the imaginary values. * - * @param re matrix object representing the real values + * @param re matrix object representing the real values + * @param threads number of threads * @return array of two matrix blocks */ - public static MatrixBlock[] fft_linearized(MatrixBlock re) { + public static MatrixBlock[] fft_linearized(MatrixBlock re, int threads) { return fft_linearized(re, - new MatrixBlock(re.getNumRows(), re.getNumColumns(), new double[re.getNumRows() * re.getNumColumns()])); + new MatrixBlock(re.getNumRows(), re.getNumColumns(), new double[re.getNumRows() * re.getNumColumns()]), + threads); } /** * Function to perform IFFT for each row of a given matrix. The given matrix only represents real values. The output * contains one matrix for the real and one for the imaginary values. * - * @param re matrix object representing the real values + * @param re matrix object representing the real values + * @param threads number of threads * @return array of two matrix blocks */ - public static MatrixBlock[] ifft_linearized(MatrixBlock re) { + public static MatrixBlock[] ifft_linearized(MatrixBlock re, int threads) { return ifft_linearized(re, - new MatrixBlock(re.getNumRows(), re.getNumColumns(), new double[re.getNumRows() * re.getNumColumns()])); + new MatrixBlock(re.getNumRows(), re.getNumColumns(), new double[re.getNumRows() * re.getNumColumns()]), + threads); + } + + private static void awaitParallelExecution(ExecutorService pool) { + + try { + int timeout = 100; + if(!pool.awaitTermination(timeout, TimeUnit.SECONDS)) { + pool.shutdownNow(); + } + } + catch(InterruptedException e) { + e.printStackTrace(); + } } } diff --git a/src/test/java/org/apache/sysds/test/component/matrix/FourierTest.java b/src/test/java/org/apache/sysds/test/component/matrix/FourierTest.java index caa9d07d4a1..9e2650a4e36 100644 --- a/src/test/java/org/apache/sysds/test/component/matrix/FourierTest.java +++ b/src/test/java/org/apache/sysds/test/component/matrix/FourierTest.java @@ -31,6 +31,8 @@ public class FourierTest { + int threads = Runtime.getRuntime().availableProcessors(); + @Test public void test_fft_one_dim() { @@ -40,7 +42,7 @@ public void test_fft_one_dim() { double[] expected_re = {6, 15, -36, 15}; double[] expected_im = {0, -15, 0, 15}; - fft(re, im); + fft(re, im, threads); assertArrayEquals(expected_re, re.getDenseBlockValues(), 0.0001); assertArrayEquals(expected_im, im.getDenseBlockValues(), 0.0001); @@ -56,7 +58,7 @@ public void test_fft_one_dim_2() { double[] expected_re = {35, 4.89949, 15, -14.89949, -45, -14.89949, 15, 4.89949}; double[] expected_im = {0, 18.58579, -16, -21.41421, 0, 21.41421, 16, -18.58579}; - fft(re, im); + fft(re, im, threads); assertArrayEquals(expected_re, re.getDenseBlockValues(), 0.0001); assertArrayEquals(expected_im, im.getDenseBlockValues(), 0.0001); @@ -71,7 +73,7 @@ public void test_fft_one_dim_matrixBlock() { double[] expected_re = {6, 15, -36, 15}; double[] expected_im = {0, -15, 0, 15}; - MatrixBlock[] res = fft(re, im); + MatrixBlock[] res = fft(re, im, threads); assertArrayEquals(expected_re, res[0].getDenseBlockValues(), 0.0001); assertArrayEquals(expected_im, res[1].getDenseBlockValues(), 0.0001); @@ -87,8 +89,8 @@ public void test_ifft_one_dim_matrixBlock_2() { MatrixBlock re = new MatrixBlock(1, 4, in_re); MatrixBlock im = new MatrixBlock(1, 4, in_im); - MatrixBlock[] inter = fft(re, im); - MatrixBlock[] res = ifft(inter[0], inter[1]); + MatrixBlock[] inter = fft(re, im, threads); + MatrixBlock[] res = ifft(inter[0], inter[1], threads); assertArrayEquals(in_re, res[0].getDenseBlockValues(), 0.0001); assertArrayEquals(in_im, res[1].getDenseBlockValues(), 0.0001); @@ -104,7 +106,7 @@ public void test_fft_two_dim_matrixBlock() { double[] expected_re = {6, -36, 30, 0}; double[] expected_im = {0, 0, 0, 0}; - MatrixBlock[] res = fft(re, im); + MatrixBlock[] res = fft(re, im, threads); assertArrayEquals(expected_re, res[0].getDenseBlockValues(), 0.0001); assertArrayEquals(expected_im, res[1].getDenseBlockValues(), 0.0001); @@ -120,7 +122,7 @@ public void test_ifft_two_dim_matrixBlock() { double[] expected_re = {0, 18, -15, 3}; double[] expected_im = {0, 0, 0, 0}; - MatrixBlock[] res = ifft(re, im); + MatrixBlock[] res = ifft(re, im, threads); assertArrayEquals(expected_re, res[0].getDenseBlockValues(), 0.0001); assertArrayEquals(expected_im, res[1].getDenseBlockValues(), 0.0001); @@ -136,7 +138,7 @@ public void test_fft_two_dim_matrixBlock_row_1() { double[] expected_re = {18, -18}; double[] expected_im = {0, 0}; - MatrixBlock[] res = fft(re, im); + MatrixBlock[] res = fft(re, im, threads); assertArrayEquals(expected_re, res[0].getDenseBlockValues(), 0.0001); assertArrayEquals(expected_im, res[1].getDenseBlockValues(), 0.0001); @@ -152,7 +154,7 @@ public void test_fft_two_dim_matrixBlock_row_2() { double[] expected_re = {-12, -18}; double[] expected_im = {0, 0}; - MatrixBlock[] res = fft(re, im); + MatrixBlock[] res = fft(re, im, threads); assertArrayEquals(expected_re, res[0].getDenseBlockValues(), 0.0001); assertArrayEquals(expected_im, res[1].getDenseBlockValues(), 0.0001); @@ -181,7 +183,7 @@ public void test_ifft_with_complex_numpy_data() { 0.10605270957897009, -0.036579082654843526, 0.07808216015046443, 0.035626994964481226, 0.018752997939582024, -0.023854392878864396, 0.07513090216687965}; - MatrixBlock[] res = ifft(re, im); + MatrixBlock[] res = ifft(re, im, threads); assertArrayEquals(expected_re, res[0].getDenseBlockValues(), 0.0001); assertArrayEquals(expected_im, res[1].getDenseBlockValues(), 0.0001); @@ -202,7 +204,7 @@ public void test_ifft_2d_with_generated_data() { double[] expected_im = {0.47402232, 0.28296348, -0.00819079, 0.0842882}; - MatrixBlock[] res = ifft(re, im); + MatrixBlock[] res = ifft(re, im, threads); assertArrayEquals(expected_re, res[0].getDenseBlockValues(), 0.0001); assertArrayEquals(expected_im, res[1].getDenseBlockValues(), 0.0001); @@ -231,7 +233,7 @@ public void test_ifft_with_real_numpy_data() { }; - MatrixBlock[] res = ifft(re, im); + MatrixBlock[] res = ifft(re, im, threads); assertArrayEquals(expected_re, res[0].getDenseBlockValues(), 0.0001); assertArrayEquals(expected_im, res[1].getDenseBlockValues(), 0.0001); @@ -300,7 +302,7 @@ public void test_fft_two_dim_8_times_8() { 1.1079219041153268, 0.1094531803830765, 3.488182265163636, -1.5698242466218544, -0.1013387045518459, 0.9269290699615746, -0.699890233104248, 3.617209720991753, -0.5565163478425035, 3.502962737763559}; - MatrixBlock[] res = fft(re, im); + MatrixBlock[] res = fft(re, im, threads); assertArrayEquals(expected_re, res[0].getDenseBlockValues(), 0.0001); assertArrayEquals(expected_im, res[1].getDenseBlockValues(), 0.0001); @@ -316,7 +318,7 @@ public void test_fft_linearized() { double[] expected_re = {6, 15, -36, 15, 6, 15, -36, 15}; double[] expected_im = {0, -15, 0, 15, 0, -15, 0, 15}; - fft_linearized(re, im); + fft_linearized(re, im, threads); assertArrayEquals(expected_re, re.getDenseBlockValues(), 0.0001); assertArrayEquals(expected_im, im.getDenseBlockValues(), 0.0001); @@ -332,8 +334,8 @@ public void test_ifft_linearized() { MatrixBlock re = new MatrixBlock(2, 4, in_re); MatrixBlock im = new MatrixBlock(2, 4, in_im); - MatrixBlock[] inter = fft_linearized(re, im); - MatrixBlock[] res = ifft_linearized(inter[0], inter[1]); + MatrixBlock[] inter = fft_linearized(re, im, threads); + MatrixBlock[] res = ifft_linearized(inter[0], inter[1], threads); assertArrayEquals(in_re, res[0].getDenseBlockValues(), 0.0001); assertArrayEquals(in_im, res[1].getDenseBlockValues(), 0.0001); From 2cc8fc364ba383e053bf5ed9cda0351855b4a9b6 Mon Sep 17 00:00:00 2001 From: Jessica Priebe Date: Sun, 18 Feb 2024 14:50:16 +0100 Subject: [PATCH 069/133] split fft_one_dim into smaller methods --- .../runtime/matrix/data/LibMatrixFourier.java | 75 +++++++++++++------ 1 file changed, 53 insertions(+), 22 deletions(-) diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java index d25678cde60..b7782e429d1 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java @@ -217,40 +217,72 @@ public static void fft_one_dim(double[] re, double[] im, double[] re_inter, doub return; int startSub = start + sub * minStep + isOdd * (step / 2); + fft_one_dim_sub(re, im, re_inter, im_inter, start, stop, startSub, subNum, step, angle); - // first iterates over even indices, then over odd indices - for(int j = startSub, cnt = 0; cnt < subNum / 2; j += 2 * step, cnt++) { + } - double omega_pow_re = FastMath.cos(cnt * angle); - double omega_pow_im = FastMath.sin(cnt * angle); + } - // calculate m using the result of odd index - double m_re = omega_pow_re * re[j + step] - omega_pow_im * im[j + step]; - double m_im = omega_pow_re * im[j + step] + omega_pow_im * re[j + step]; + } - int index = startSub + cnt * step; - re_inter[index] = re[j] + m_re; - re_inter[index + (stop - start) / 2] = re[j] - m_re; + } - im_inter[index] = im[j] + m_im; - im_inter[index + (stop - start) / 2] = im[j] - m_im; - } + /** + * Function to perform one-dimensional FFT for subArrays of two given double arrays. The first one represents the + * real values and the second one the imaginary values. Both arrays get updated and contain the result. + * + * @param re array representing the real values + * @param im array representing the imaginary values + * @param re_inter array for the real values of intermediate results + * @param im_inter array for the imaginary values of intermediate results + * @param start start index (incl.) + * @param stop stop index (excl.) + * @param startSub start index of subarray (incl.) + * @param subNum number of elements in subarray + * @param step step size between elements in subarray + * @param angle angle + */ + private static void fft_one_dim_sub(double[] re, double[] im, double[] re_inter, double[] im_inter, int start, + int stop, int startSub, int subNum, int step, double angle) { - for(int j = startSub; j < startSub + (stop - start); j += step) { - re[j] = re_inter[j]; - im[j] = im_inter[j]; - re_inter[j] = 0; - im_inter[j] = 0; - } + for(int j = startSub, cnt = 0; cnt < subNum / 2; j += 2 * step, cnt++) { - } + double omega_pow_re = FastMath.cos(cnt * angle); + double omega_pow_im = FastMath.sin(cnt * angle); - } + // calculate m using the result of odd index + double m_re = omega_pow_re * re[j + step] - omega_pow_im * im[j + step]; + double m_im = omega_pow_re * im[j + step] + omega_pow_im * re[j + step]; + + int index = startSub + cnt * step; + re_inter[index] = re[j] + m_re; + re_inter[index + (stop - start) / 2] = re[j] - m_re; + im_inter[index] = im[j] + m_im; + im_inter[index + (stop - start) / 2] = im[j] - m_im; } + for(int j = startSub; j < startSub + (stop - start); j += step) { + re[j] = re_inter[j]; + im[j] = im_inter[j]; + re_inter[j] = 0; + im_inter[j] = 0; + } } + /** + * Function to perform one-dimensional IFFT for two given double arrays. The first one represents the real values + * and the second one the imaginary values. Both arrays get updated and contain the result. + * + * @param re array representing the real values + * @param im array representing the imaginary values + * @param re_inter array for the real values of intermediate results + * @param im_inter array for the imaginary values of intermediate results + * @param start start index (incl.) + * @param stop stop index (excl.) + * @param num number of values used for fft + * @param minStep step size between values used for fft + */ private static void ifft_one_dim(double[] re, double[] im, double[] re_inter, double[] im_inter, int start, int stop, int num, int minStep) { @@ -260,7 +292,6 @@ private static void ifft_one_dim(double[] re, double[] im, double[] re_inter, do } // apply fft - // fft_one_dim_recursive(re, im, re_inter, im_inter, start, step, num, subArraySize); fft_one_dim(re, im, re_inter, im_inter, start, stop, num, minStep); // conjugate and scale result From 9b94f25168d36cf4734f203a9e8158be3bcb5d24 Mon Sep 17 00:00:00 2001 From: Frederic Zoepffel Date: Mon, 19 Feb 2024 17:31:50 +0100 Subject: [PATCH 070/133] threading improved --- .../runtime/matrix/data/LibMatrixFourier.java | 52 +++++++++++++++---- 1 file changed, 41 insertions(+), 11 deletions(-) diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java index b7782e429d1..6fa29b16a7f 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java @@ -23,7 +23,12 @@ import org.apache.sysds.runtime.util.CommonThreadPool; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; +import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; public class LibMatrixFourier { @@ -130,20 +135,45 @@ public static void fft(double[] re, double[] im, int rows, int cols, int threads double[] re_inter = new double[rows * cols]; double[] im_inter = new double[rows * cols]; - ExecutorService pool = CommonThreadPool.get(threads); + final ExecutorService pool = CommonThreadPool.get(threads); - for(int i = 0; i < rows; i++) { - final int finalI = i; - pool.submit(() -> fft_one_dim(re, im, re_inter, im_inter, finalI * cols, (finalI + 1) * cols, cols, 1)); - } - awaitParallelExecution(pool); + final List> tasks = new ArrayList<>(); - if(inclColCalc && rows > 1) { - for(int j = 0; j < cols; j++) { - final int finalJ = j; - pool.submit(() -> fft_one_dim(re, im, re_inter, im_inter, finalJ, finalJ + rows * cols, rows, cols)); + try { + final int rBlz = Math.max(rows / threads, 32); + + for(int i = 0; i < rows; i += rBlz){ + final int start = i; + final int end = Math.min(i + rBlz, rows); + + tasks.add( pool.submit(() -> { + for(int j = start; j < end; j++) { + final int finalI = j; + fft_one_dim(re, im, re_inter, im_inter, finalI * cols, (finalI + 1) * cols, cols, 1); + } + })); } - awaitParallelExecution(pool); + + for(Future f : tasks) + f.get(); + + tasks.clear(); + if(inclColCalc && rows > 1) { + for(int j = 0; j < cols; j++) { + final int finalJ = j; + tasks.add( pool.submit(() -> + fft_one_dim(re, im, re_inter, im_inter, finalJ, finalJ + rows * cols, rows, cols))); + } + } + + for(Future f : tasks) + f.get(); + + } catch (InterruptedException | ExecutionException e) { + throw new RuntimeException(e); + } + finally{ + pool.shutdown(); } } From 8bed34c0df34023d9150bb729b3629b8e87e650d Mon Sep 17 00:00:00 2001 From: Jessica Priebe Date: Mon, 19 Feb 2024 18:21:39 +0100 Subject: [PATCH 071/133] improved parallelization --- .../runtime/matrix/data/LibMatrixFourier.java | 109 +++++++++++------- 1 file changed, 66 insertions(+), 43 deletions(-) diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java index 6fa29b16a7f..de83015fb3e 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java @@ -23,13 +23,11 @@ import org.apache.sysds.runtime.util.CommonThreadPool; - -import java.util.ArrayList; import java.util.List; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.ExecutorService; +import java.util.ArrayList; import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.ExecutionException; public class LibMatrixFourier { @@ -141,38 +139,44 @@ public static void fft(double[] re, double[] im, int rows, int cols, int threads try { final int rBlz = Math.max(rows / threads, 32); + final int cBlz = Math.max(cols / threads, 32); - for(int i = 0; i < rows; i += rBlz){ + for(int i = 0; i < rows; i += rBlz) { final int start = i; final int end = Math.min(i + rBlz, rows); - tasks.add( pool.submit(() -> { + tasks.add(pool.submit(() -> { for(int j = start; j < end; j++) { - final int finalI = j; - fft_one_dim(re, im, re_inter, im_inter, finalI * cols, (finalI + 1) * cols, cols, 1); + fft_one_dim(re, im, re_inter, im_inter, j * cols, (j + 1) * cols, cols, 1); } })); } for(Future f : tasks) f.get(); - + tasks.clear(); if(inclColCalc && rows > 1) { - for(int j = 0; j < cols; j++) { - final int finalJ = j; - tasks.add( pool.submit(() -> - fft_one_dim(re, im, re_inter, im_inter, finalJ, finalJ + rows * cols, rows, cols))); + for(int j = 0; j < cols; j += cBlz) { + final int start = j; + final int end = Math.min(j + cBlz, cols); + + tasks.add(pool.submit(() -> { + for(int i = start; i < end; i++) { + fft_one_dim(re, im, re_inter, im_inter, i, i + rows * cols, rows, cols); + } + })); } + + for(Future f : tasks) + f.get(); } - - for(Future f : tasks) - f.get(); - - } catch (InterruptedException | ExecutionException e) { + + } + catch(InterruptedException | ExecutionException e) { throw new RuntimeException(e); } - finally{ + finally { pool.shutdown(); } @@ -196,19 +200,51 @@ public static void ifft(double[] re, double[] im, int rows, int cols, int thread ExecutorService pool = CommonThreadPool.get(threads); - if(inclColCalc && rows > 1) { - for(int j = 0; j < cols; j++) { - final int finalJ = j; - pool.submit(() -> ifft_one_dim(re, im, re_inter, im_inter, finalJ, finalJ + rows * cols, rows, cols)); + final List> tasks = new ArrayList<>(); + + try { + final int rBlz = Math.max(rows / threads, 32); + final int cBlz = Math.max(cols / threads, 32); + + if(inclColCalc && rows > 1) { + + for(int j = 0; j < cols; j += cBlz) { + final int start = j; + final int end = Math.min(j + cBlz, cols); + + tasks.add(pool.submit(() -> { + for(int i = start; i < end; i++) { + ifft_one_dim(re, im, re_inter, im_inter, i, i + rows * cols, rows, cols); + } + })); + } + + for(Future f : tasks) + f.get(); } - awaitParallelExecution(pool); - } - for(int i = 0; i < rows; i++) { - final int finalI = i; - pool.submit(() -> ifft_one_dim(re, im, re_inter, im_inter, finalI * cols, (finalI + 1) * cols, cols, 1)); + tasks.clear(); + for(int i = 0; i < rows; i += rBlz) { + final int start = i; + final int end = Math.min(i + rBlz, rows); + + tasks.add(pool.submit(() -> { + for(int j = start; j < end; j++) { + ifft_one_dim(re, im, re_inter, im_inter, j * cols, (j + 1) * cols, cols, 1); + } + })); + } + + for(Future f : tasks) + f.get(); + + } + catch(InterruptedException | ExecutionException e) { + throw new RuntimeException(e); + } + finally { + pool.shutdown(); } - awaitParallelExecution(pool); } @@ -398,17 +434,4 @@ public static MatrixBlock[] ifft_linearized(MatrixBlock re, int threads) { threads); } - private static void awaitParallelExecution(ExecutorService pool) { - - try { - int timeout = 100; - if(!pool.awaitTermination(timeout, TimeUnit.SECONDS)) { - pool.shutdownNow(); - } - } - catch(InterruptedException e) { - e.printStackTrace(); - } - } - } From ca839c9e4467c3c9992d4c841e9dc8ec5ca351d9 Mon Sep 17 00:00:00 2001 From: Jessica Priebe Date: Mon, 19 Feb 2024 19:27:08 +0100 Subject: [PATCH 072/133] split fft_one_dim into smaller methods --- .../runtime/matrix/data/LibMatrixFourier.java | 54 ++++++++++--------- 1 file changed, 30 insertions(+), 24 deletions(-) diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java index de83015fb3e..26bbf8b6faf 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java @@ -134,7 +134,6 @@ public static void fft(double[] re, double[] im, int rows, int cols, int threads double[] im_inter = new double[rows * cols]; final ExecutorService pool = CommonThreadPool.get(threads); - final List> tasks = new ArrayList<>(); try { @@ -144,7 +143,6 @@ public static void fft(double[] re, double[] im, int rows, int cols, int threads for(int i = 0; i < rows; i += rBlz) { final int start = i; final int end = Math.min(i + rBlz, rows); - tasks.add(pool.submit(() -> { for(int j = start; j < end; j++) { fft_one_dim(re, im, re_inter, im_inter, j * cols, (j + 1) * cols, cols, 1); @@ -160,7 +158,6 @@ public static void fft(double[] re, double[] im, int rows, int cols, int threads for(int j = 0; j < cols; j += cBlz) { final int start = j; final int end = Math.min(j + cBlz, cols); - tasks.add(pool.submit(() -> { for(int i = start; i < end; i++) { fft_one_dim(re, im, re_inter, im_inter, i, i + rows * cols, rows, cols); @@ -171,7 +168,6 @@ public static void fft(double[] re, double[] im, int rows, int cols, int threads for(Future f : tasks) f.get(); } - } catch(InterruptedException | ExecutionException e) { throw new RuntimeException(e); @@ -199,7 +195,6 @@ public static void ifft(double[] re, double[] im, int rows, int cols, int thread double[] im_inter = new double[rows * cols]; ExecutorService pool = CommonThreadPool.get(threads); - final List> tasks = new ArrayList<>(); try { @@ -207,11 +202,9 @@ public static void ifft(double[] re, double[] im, int rows, int cols, int thread final int cBlz = Math.max(cols / threads, 32); if(inclColCalc && rows > 1) { - for(int j = 0; j < cols; j += cBlz) { final int start = j; final int end = Math.min(j + cBlz, cols); - tasks.add(pool.submit(() -> { for(int i = start; i < end; i++) { ifft_one_dim(re, im, re_inter, im_inter, i, i + rows * cols, rows, cols); @@ -227,7 +220,6 @@ public static void ifft(double[] re, double[] im, int rows, int cols, int thread for(int i = 0; i < rows; i += rBlz) { final int start = i; final int end = Math.min(i + rBlz, rows); - tasks.add(pool.submit(() -> { for(int j = start; j < end; j++) { ifft_one_dim(re, im, re_inter, im_inter, j * cols, (j + 1) * cols, cols, 1); @@ -237,7 +229,6 @@ public static void ifft(double[] re, double[] im, int rows, int cols, int thread for(Future f : tasks) f.get(); - } catch(InterruptedException | ExecutionException e) { throw new RuntimeException(e); @@ -270,27 +261,43 @@ public static void fft_one_dim(double[] re, double[] im, double[] re_inter, doub // even indices for(int step = minStep * (num / 2), subNum = 2; subNum <= num; step /= 2, subNum *= 2) { - double angle = -2 * FastMath.PI / subNum; + fft_one_dim_iter_sub(re, im, re_inter, im_inter, start, stop, num, minStep, subNum, step, angle); + } + } - // use ceil for the main (sub)array - for(int sub = 0; sub < FastMath.ceil(num / (2 * (double) subNum)); sub++) { - - for(int isOdd = 0; isOdd < 2; isOdd++) { - - // no odd values for main (sub)array - if(isOdd == 1 && subNum == num) - return; + /** + * Function to call one-dimensional FFT for all subArrays of two given double arrays. The first one represents the + * real values and the second one the imaginary values. Both arrays get updated and contain the result. + * + * @param re array representing the real values + * @param im array representing the imaginary values + * @param re_inter array for the real values of intermediate results + * @param im_inter array for the imaginary values of intermediate results + * @param start start index (incl.) + * @param stop stop index (excl.) + * @param num number of values used for fft + * @param minStep step size between values used for fft + * @param subNum number of elements in subarray + * @param step step size between elements in subarray + * @param angle angle + */ + private static void fft_one_dim_iter_sub(double[] re, double[] im, double[] re_inter, double[] im_inter, int start, + int stop, int num, int minStep, int subNum, int step, double angle) { - int startSub = start + sub * minStep + isOdd * (step / 2); - fft_one_dim_sub(re, im, re_inter, im_inter, start, stop, startSub, subNum, step, angle); + // use ceil for the main (sub)array + for(int sub = 0; sub < FastMath.ceil(num / (2 * (double) subNum)); sub++) { - } + int startSub = start + sub * minStep; + fft_one_dim_sub(re, im, re_inter, im_inter, start, stop, startSub, subNum, step, angle); - } + // no odd values for main (sub)array + if(subNum == num) + return; + startSub = start + sub * minStep + step / 2; + fft_one_dim_sub(re, im, re_inter, im_inter, start, stop, startSub, subNum, step, angle); } - } /** @@ -365,7 +372,6 @@ private static void ifft_one_dim(double[] re, double[] im, double[] re_inter, do re[i] = re[i] / num; im[i] = -im[i] / num; } - } /** From eaa57985a397a1f3fc1040c9958c78d45b6d59f6 Mon Sep 17 00:00:00 2001 From: Jessica Priebe Date: Mon, 19 Feb 2024 20:24:03 +0100 Subject: [PATCH 073/133] added caching --- .../runtime/matrix/data/LibMatrixFourier.java | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java index 26bbf8b6faf..cc2cddd25b5 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java @@ -24,6 +24,7 @@ import org.apache.sysds.runtime.util.CommonThreadPool; import java.util.List; +import java.util.HashMap; import java.util.ArrayList; import java.util.concurrent.Future; import java.util.concurrent.ExecutorService; @@ -31,6 +32,9 @@ public class LibMatrixFourier { + static HashMap sinCache = new HashMap<>(); + static HashMap cosCache = new HashMap<>(); + /** * Function to perform FFT for two given matrices. The first one represents the real values and the second one the * imaginary values. The output also contains one matrix for the real and one for the imaginary values. @@ -320,8 +324,8 @@ private static void fft_one_dim_sub(double[] re, double[] im, double[] re_inter, for(int j = startSub, cnt = 0; cnt < subNum / 2; j += 2 * step, cnt++) { - double omega_pow_re = FastMath.cos(cnt * angle); - double omega_pow_im = FastMath.sin(cnt * angle); + double omega_pow_re = cos(cnt * angle); + double omega_pow_im = sin(cnt * angle); // calculate m using the result of odd index double m_re = omega_pow_re * re[j + step] - omega_pow_im * im[j + step]; @@ -440,4 +444,18 @@ public static MatrixBlock[] ifft_linearized(MatrixBlock re, int threads) { threads); } + private static double sin(double angle){ + if (!sinCache.containsKey(angle)) { + sinCache.put(angle, FastMath.sin(angle)); + } + return sinCache.get(angle); + } + + private static double cos(double angle){ + if (!cosCache.containsKey(angle)) { + cosCache.put(angle, FastMath.cos(angle)); + } + return cosCache.get(angle); + } + } From ec92b8f60c2201fb08c79228645a4d76332e21d5 Mon Sep 17 00:00:00 2001 From: Jessica Priebe Date: Tue, 20 Feb 2024 10:54:33 +0100 Subject: [PATCH 074/133] adjusted caching --- .../runtime/matrix/data/LibMatrixFourier.java | 46 ++++++++++++++----- 1 file changed, 34 insertions(+), 12 deletions(-) diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java index cc2cddd25b5..5aefd73c4c8 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java @@ -23,6 +23,7 @@ import org.apache.sysds.runtime.util.CommonThreadPool; +import java.lang.ref.SoftReference; import java.util.List; import java.util.HashMap; import java.util.ArrayList; @@ -32,8 +33,8 @@ public class LibMatrixFourier { - static HashMap sinCache = new HashMap<>(); - static HashMap cosCache = new HashMap<>(); + static SoftReference> sinCacheRef = new SoftReference<>(new HashMap<>()); + static SoftReference> cosCacheRef = new SoftReference<>(new HashMap<>()); /** * Function to perform FFT for two given matrices. The first one represents the real values and the second one the @@ -322,10 +323,18 @@ private static void fft_one_dim_iter_sub(double[] re, double[] im, double[] re_i private static void fft_one_dim_sub(double[] re, double[] im, double[] re_inter, double[] im_inter, int start, int stop, int startSub, int subNum, int step, double angle) { + HashMap sinCache = sinCacheRef.get(); + HashMap cosCache = cosCacheRef.get(); + + if(sinCache == null) + sinCache = new HashMap<>(); + if(cosCache == null) + cosCache = new HashMap<>(); + for(int j = startSub, cnt = 0; cnt < subNum / 2; j += 2 * step, cnt++) { - double omega_pow_re = cos(cnt * angle); - double omega_pow_im = sin(cnt * angle); + double omega_pow_re = cos(cnt * angle, cosCache); + double omega_pow_im = sin(cnt * angle, sinCache); // calculate m using the result of odd index double m_re = omega_pow_re * re[j + step] - omega_pow_im * im[j + step]; @@ -444,18 +453,31 @@ public static MatrixBlock[] ifft_linearized(MatrixBlock re, int threads) { threads); } - private static double sin(double angle){ - if (!sinCache.containsKey(angle)) { - sinCache.put(angle, FastMath.sin(angle)); + private static double sin(double angle, HashMap cache) { + + if(!cache.containsKey(angle)) { + cache.put(angle, FastMath.sin(angle)); + limitCache(cache); } - return sinCache.get(angle); + + return cache.get(angle); + } + + private static double cos(double angle, HashMap cache) { + + if(!cache.containsKey(angle)) { + cache.put(angle, FastMath.cos(angle)); + limitCache(cache); + } + + return cache.get(angle); } - private static double cos(double angle){ - if (!cosCache.containsKey(angle)) { - cosCache.put(angle, FastMath.cos(angle)); + private static void limitCache(HashMap cache) { + if(cache.size() > 1000) { + // remove oldest key + cache.remove(cache.keySet().iterator().next()); } - return cosCache.get(angle); } } From 931b78d44237d18b864b7681f29b417c84afdfa1 Mon Sep 17 00:00:00 2001 From: Jessica Priebe Date: Tue, 20 Feb 2024 11:00:46 +0100 Subject: [PATCH 075/133] formatting --- .../apache/sysds/runtime/matrix/data/LibMatrixFourier.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java index 5aefd73c4c8..87afcfb2817 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java @@ -454,22 +454,18 @@ public static MatrixBlock[] ifft_linearized(MatrixBlock re, int threads) { } private static double sin(double angle, HashMap cache) { - if(!cache.containsKey(angle)) { cache.put(angle, FastMath.sin(angle)); limitCache(cache); } - return cache.get(angle); } private static double cos(double angle, HashMap cache) { - if(!cache.containsKey(angle)) { cache.put(angle, FastMath.cos(angle)); limitCache(cache); } - return cache.get(angle); } From 8fe1f3bc3aef0c40ddc93b8a5d55c142ec9ff43c Mon Sep 17 00:00:00 2001 From: mufwan Date: Sat, 20 Jan 2024 23:18:45 +0100 Subject: [PATCH 076/133] Your commit message --- .../runtime/matrix/data/LibMatrixSTFT.java | 96 ++++++++++++ .../sysds/test/component/matrix/STFTTest.java | 142 ++++++++++++++++++ 2 files changed, 238 insertions(+) create mode 100644 src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixSTFT.java create mode 100644 src/test/java/org/apache/sysds/test/component/matrix/STFTTest.java diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixSTFT.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixSTFT.java new file mode 100644 index 00000000000..b0e368ce8d0 --- /dev/null +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixSTFT.java @@ -0,0 +1,96 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.sysds.runtime.matrix.data; + +import static org.apache.sysds.runtime.matrix.data.LibMatrixFourier.fft_one_dim; + +public class LibMatrixSTFT { + + /** + * Function to perform STFT on two given matrices with windowSize and overlap. + * The first one represents the real values and the second one the imaginary values. + * The output also contains one matrix for the real and one for the imaginary values. + * The results of the fourier transformations are appended to each other in the output. + * + * @param re matrix object representing the real values + * @param im matrix object representing the imaginary values + * @param windowSize size of window + * @param overlap size of overlap + * @return array of two matrix blocks + */ + public static MatrixBlock[] stft(MatrixBlock re, MatrixBlock im, int windowSize, int overlap) { + return stft(re.getDenseBlockValues(), windowSize, overlap); + } + + /** + * Function to perform STFT on two given matrices with windowSize and overlap. + * The first one represents the real values and the second one the imaginary values. + * The output also contains one matrix for the real and one for the imaginary values. + * The results of the fourier transformations are appended to each other in the output. + * + * @param signal array representing the signal + * @param windowSize size of window + * @param overlap size of overlap + * @return array of two matrix blocks + */ + public static MatrixBlock[] stft(double[] signal, int windowSize, int overlap) { + + if (windowSize < 1 || (windowSize & (windowSize - 1)) != 0) { + throw new IllegalArgumentException("windowSize is not a power of two"); + } + int stepSize = windowSize - overlap; + int totalFrames = (signal.length - overlap + stepSize - 1) / stepSize; + int out_len = totalFrames * windowSize; + + // Initialize the STFT output array: [time frame][frequency bin][real & imaginary parts] + double[] stftOutput_re = new double[out_len]; + double[] stftOutput_im = new double[out_len]; + + for (int i = 0; i < totalFrames; i++) { + for (int j = 0; j < windowSize; j++) { + stftOutput_re[i * windowSize + j] = ((i * stepSize + j) < signal.length) ? signal[i * stepSize + j] : 0; + } + } + + for (int frame = 0; frame < totalFrames; frame++) { + // Perform FFT on windowed signal + double[] re_inter = new double[out_len]; + double[] im_inter = new double[out_len]; + fft_one_dim(stftOutput_re, stftOutput_im, re_inter, im_inter, frame * windowSize, 1, windowSize, windowSize); + } + return new MatrixBlock[]{new MatrixBlock(1, out_len, stftOutput_re), new MatrixBlock(1, out_len, stftOutput_im)}; + } + + /** + * Function to perform STFT on two given matrices with windowSize and overlap. + * The first one represents the real values and the second one the imaginary values. + * The output also contains one matrix for the real and one for the imaginary values. + * The results of the fourier transformations are appended to each other in the output. + * + * @param in matrix object representing the real values + * @param windowSize size of window + * @param overlap size of overlap + * @return array of two matrix blocks + */ + public static MatrixBlock[] stft(MatrixBlock[] in, int windowSize, int overlap){ + return stft(in[0].getDenseBlockValues(), windowSize, overlap); + } + +} diff --git a/src/test/java/org/apache/sysds/test/component/matrix/STFTTest.java b/src/test/java/org/apache/sysds/test/component/matrix/STFTTest.java new file mode 100644 index 00000000000..aebe7c31458 --- /dev/null +++ b/src/test/java/org/apache/sysds/test/component/matrix/STFTTest.java @@ -0,0 +1,142 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.sysds.test.component.matrix; + +import org.apache.sysds.runtime.matrix.data.MatrixBlock; +import org.junit.Test; + +import static org.junit.Assert.assertArrayEquals; +import static org.apache.sysds.runtime.matrix.data.LibMatrixSTFT.stft; + +public class STFTTest { + + @Test + public void simple_test() { + + double[] signal = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; + int windowSize = 4; + int overlap = 2; + MatrixBlock[] res = stft(signal, windowSize, overlap); + double[] res_re = res[0].getDenseBlockValues(); + double[] res_im = res[1].getDenseBlockValues(); + + + double[]expected_re = {6, -2, -2, -2, 14, -2, -2, -2, 22, -2, -2, -2, 30, -2, -2, -2, 38, -2, -2, -2, 46, -2, -2, -2, 54, -2, -2, -2}; + + double[]expected_im = {0, 2, 0, -2, 0, 2, 0, -2, 0, 2, 0, -2, 0, 2, 0, -2, 0, 2, 0, -2, 0, 2, 0, -2, 0, 2, 0, -2}; + + + for(double elem : res_re){ + System.out.print(elem + " "); + } + + for(double elem : res_re){ + System.out.print(elem+" "); + } + System.out.println(); + for(double elem : res_im){ + System.out.print(elem+" "); + } + + + assertArrayEquals(expected_re, res_re, 0.0001); + assertArrayEquals(expected_im, res_im, 0.0001); + + } + + @Test + public void matrix_block_one_dim_test(){ + + double[] in = {0, 18, -15, 3}; + + double[] expected_re = {6, 15, -36, 15}; + double[] expected_im = {0, -15, 0, 15}; + + MatrixBlock[] res = stft(in, 4, 0); + double[] res_re = res[0].getDenseBlockValues(); + double[] res_im = res[1].getDenseBlockValues(); + + for(double elem : res_re){ + System.out.print(elem+" "); + } + System.out.println(); + for(double elem : res_im){ + System.out.print(elem+" "); + } + + assertArrayEquals(expected_re, res_re, 0.0001); + assertArrayEquals(expected_im, res_im, 0.0001); + + } + + @Test + public void matrix_block_one_dim_test2(){ + + double[] in = {10, 5, -3, 8, 15, -6, 2, 0}; + + double[] expected_re = {20.0, 13.0, -6.0, 13.0, 14.0, -18.0, 10.0, -18.0, 11.0, 13.0, 23.0, 13.0}; + double[] expected_im = {0.0, 3.0, 0.0, -3.0, 0.0, -14.0, 0.0, 14.0, 0.0, 6.0, 0.0, -6.0}; + + MatrixBlock[] res = stft(in, 4, 2); + double[] res_re = res[0].getDenseBlockValues(); + double[] res_im = res[1].getDenseBlockValues(); + + for(double elem : res_re){ + System.out.print(elem+" "); + } + System.out.println(); + for(double elem : res_im){ + System.out.print(elem+" "); + } + + assertArrayEquals(expected_re, res_re, 0.0001); + assertArrayEquals(expected_im, res_im, 0.0001); + + } + + @Test + public void matrix_block_one_dim_test3(){ + + //double[] in = {10, 5, -3, 8, 15, -6, 2, 0}; + + MatrixBlock re = new MatrixBlock(1, 8, new double[]{10, 5, -3, 8, 15, -6, 2, 0}); + MatrixBlock im = new MatrixBlock(1, 8, new double[8]); + + double[] expected_re = {20.0, 13.0, -6.0, 13.0, 14.0, -18.0, 10.0, -18.0, 11.0, 13.0, 23.0, 13.0}; + double[] expected_im = {0.0, 3.0, 0.0, -3.0, 0.0, -14.0, 0.0, 14.0, 0.0, 6.0, 0.0, -6.0}; + + MatrixBlock[] res = stft(re, im, 4, 2); + double[] res_re = res[0].getDenseBlockValues(); + double[] res_im = res[1].getDenseBlockValues(); + + for(double elem : res_re){ + System.out.print(elem+" "); + } + System.out.println(); + for(double elem : res_im){ + System.out.print(elem+" "); + } + + assertArrayEquals(expected_re, res_re, 0.0001); + assertArrayEquals(expected_im, res_im, 0.0001); + + } + +} From 5a1c2b6241ddda2a840b5d08ef3c9b1e6d3828ec Mon Sep 17 00:00:00 2001 From: Jessica Priebe Date: Sun, 21 Jan 2024 14:43:06 +0100 Subject: [PATCH 077/133] inter arrays outside of loop + tab --- .../runtime/matrix/data/LibMatrixSTFT.java | 130 +++++++++--------- 1 file changed, 67 insertions(+), 63 deletions(-) diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixSTFT.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixSTFT.java index b0e368ce8d0..b3f34a9717a 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixSTFT.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixSTFT.java @@ -20,77 +20,81 @@ package org.apache.sysds.runtime.matrix.data; import static org.apache.sysds.runtime.matrix.data.LibMatrixFourier.fft_one_dim; +import static org.apache.sysds.runtime.matrix.data.LibMatrixFourier.isPowerOfTwo; public class LibMatrixSTFT { - /** - * Function to perform STFT on two given matrices with windowSize and overlap. - * The first one represents the real values and the second one the imaginary values. - * The output also contains one matrix for the real and one for the imaginary values. - * The results of the fourier transformations are appended to each other in the output. - * - * @param re matrix object representing the real values - * @param im matrix object representing the imaginary values - * @param windowSize size of window - * @param overlap size of overlap - * @return array of two matrix blocks - */ - public static MatrixBlock[] stft(MatrixBlock re, MatrixBlock im, int windowSize, int overlap) { - return stft(re.getDenseBlockValues(), windowSize, overlap); - } + /** + * Function to perform STFT on two given matrices with windowSize and overlap. + * The first one represents the real values and the second one the imaginary values. + * The output also contains one matrix for the real and one for the imaginary values. + * The results of the fourier transformations are appended to each other in the output. + * + * @param re matrix object representing the real values + * @param im matrix object representing the imaginary values + * @param windowSize size of window + * @param overlap size of overlap + * @return array of two matrix blocks + */ + public static MatrixBlock[] stft(MatrixBlock re, MatrixBlock im, int windowSize, int overlap) { + return stft(re.getDenseBlockValues(), windowSize, overlap); + } - /** - * Function to perform STFT on two given matrices with windowSize and overlap. - * The first one represents the real values and the second one the imaginary values. - * The output also contains one matrix for the real and one for the imaginary values. - * The results of the fourier transformations are appended to each other in the output. - * - * @param signal array representing the signal - * @param windowSize size of window - * @param overlap size of overlap - * @return array of two matrix blocks - */ - public static MatrixBlock[] stft(double[] signal, int windowSize, int overlap) { + /** + * Function to perform STFT on two given matrices with windowSize and overlap. + * The first one represents the real values and the second one the imaginary values. + * The output also contains one matrix for the real and one for the imaginary values. + * The results of the fourier transformations are appended to each other in the output. + * + * @param signal array representing the signal + * @param windowSize size of window + * @param overlap size of overlap + * @return array of two matrix blocks + */ + public static MatrixBlock[] stft(double[] signal, int windowSize, int overlap) { - if (windowSize < 1 || (windowSize & (windowSize - 1)) != 0) { - throw new IllegalArgumentException("windowSize is not a power of two"); - } - int stepSize = windowSize - overlap; - int totalFrames = (signal.length - overlap + stepSize - 1) / stepSize; - int out_len = totalFrames * windowSize; + if (!isPowerOfTwo(windowSize)) { + throw new IllegalArgumentException("windowSize is not a power of two"); + } - // Initialize the STFT output array: [time frame][frequency bin][real & imaginary parts] - double[] stftOutput_re = new double[out_len]; - double[] stftOutput_im = new double[out_len]; + int stepSize = windowSize - overlap; + int totalFrames = (signal.length - overlap + stepSize - 1) / stepSize; + int out_len = totalFrames * windowSize; - for (int i = 0; i < totalFrames; i++) { - for (int j = 0; j < windowSize; j++) { - stftOutput_re[i * windowSize + j] = ((i * stepSize + j) < signal.length) ? signal[i * stepSize + j] : 0; - } - } + // Initialize the STFT output array: [time frame][frequency bin][real & imaginary parts] + double[] stftOutput_re = new double[out_len]; + double[] stftOutput_im = new double[out_len]; - for (int frame = 0; frame < totalFrames; frame++) { - // Perform FFT on windowed signal - double[] re_inter = new double[out_len]; - double[] im_inter = new double[out_len]; - fft_one_dim(stftOutput_re, stftOutput_im, re_inter, im_inter, frame * windowSize, 1, windowSize, windowSize); - } - return new MatrixBlock[]{new MatrixBlock(1, out_len, stftOutput_re), new MatrixBlock(1, out_len, stftOutput_im)}; - } + double[] re_inter = new double[out_len]; + double[] im_inter = new double[out_len]; - /** - * Function to perform STFT on two given matrices with windowSize and overlap. - * The first one represents the real values and the second one the imaginary values. - * The output also contains one matrix for the real and one for the imaginary values. - * The results of the fourier transformations are appended to each other in the output. - * - * @param in matrix object representing the real values - * @param windowSize size of window - * @param overlap size of overlap - * @return array of two matrix blocks - */ - public static MatrixBlock[] stft(MatrixBlock[] in, int windowSize, int overlap){ - return stft(in[0].getDenseBlockValues(), windowSize, overlap); - } + for (int i = 0; i < totalFrames; i++) { + for (int j = 0; j < windowSize; j++) { + stftOutput_re[i * windowSize + j] = ((i * stepSize + j) < signal.length) ? signal[i * stepSize + j] : 0; + } + } + + for (int frame = 0; frame < totalFrames; frame++) { + // Perform FFT on windowed signal + fft_one_dim(stftOutput_re, stftOutput_im, re_inter, im_inter, frame * windowSize, 1, windowSize, windowSize); + } + + return new MatrixBlock[]{new MatrixBlock(1, out_len, stftOutput_re), new MatrixBlock(1, out_len, stftOutput_im)}; + } + + /** + * Function to perform STFT on two given matrices with windowSize and overlap. + * The first one represents the real values and the second one the imaginary values. + * The output also contains one matrix for the real and one for the imaginary values. + * The results of the fourier transformations are appended to each other in the output. + * + * @param in matrix object representing the real values + * @param windowSize size of window + * @param overlap size of overlap + * @return array of two matrix blocks + */ + public static MatrixBlock[] stft(MatrixBlock[] in, int windowSize, int overlap){ + return stft(in[0].getDenseBlockValues(), windowSize, overlap); + } } From a2b08e28f3577fa2e39c5b09d5e1d45c703cee45 Mon Sep 17 00:00:00 2001 From: Jessica Priebe Date: Sun, 21 Jan 2024 23:03:13 +0100 Subject: [PATCH 078/133] adjusted to iterative fft impl --- .../org/apache/sysds/runtime/matrix/data/LibMatrixSTFT.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixSTFT.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixSTFT.java index b3f34a9717a..3458cc79cdf 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixSTFT.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixSTFT.java @@ -76,7 +76,7 @@ public static MatrixBlock[] stft(double[] signal, int windowSize, int overlap) { for (int frame = 0; frame < totalFrames; frame++) { // Perform FFT on windowed signal - fft_one_dim(stftOutput_re, stftOutput_im, re_inter, im_inter, frame * windowSize, 1, windowSize, windowSize); + fft_one_dim(stftOutput_re, stftOutput_im, re_inter, im_inter, frame * windowSize, (frame+1) * windowSize, windowSize, 1); } return new MatrixBlock[]{new MatrixBlock(1, out_len, stftOutput_re), new MatrixBlock(1, out_len, stftOutput_im)}; From 714eb46efd4cb48272d01fb13fa57516d6e1d53d Mon Sep 17 00:00:00 2001 From: Jessica Priebe Date: Mon, 22 Jan 2024 13:50:02 +0100 Subject: [PATCH 079/133] stft complex values --- .../runtime/matrix/data/LibMatrixSTFT.java | 26 ++-- .../sysds/test/component/matrix/STFTTest.java | 129 ++++++------------ 2 files changed, 59 insertions(+), 96 deletions(-) diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixSTFT.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixSTFT.java index 3458cc79cdf..07792222d53 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixSTFT.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixSTFT.java @@ -37,28 +37,29 @@ public class LibMatrixSTFT { * @return array of two matrix blocks */ public static MatrixBlock[] stft(MatrixBlock re, MatrixBlock im, int windowSize, int overlap) { - return stft(re.getDenseBlockValues(), windowSize, overlap); + return stft(re.getDenseBlockValues(), im.getDenseBlockValues(), windowSize, overlap); } /** - * Function to perform STFT on two given matrices with windowSize and overlap. + * Function to perform STFT on two given double arrays with windowSize and overlap. * The first one represents the real values and the second one the imaginary values. * The output also contains one matrix for the real and one for the imaginary values. * The results of the fourier transformations are appended to each other in the output. * - * @param signal array representing the signal + * @param signal_re array representing the real part of the signal + * @param signal_im array representing the imaginary part of the signal * @param windowSize size of window * @param overlap size of overlap * @return array of two matrix blocks */ - public static MatrixBlock[] stft(double[] signal, int windowSize, int overlap) { + public static MatrixBlock[] stft(double[] signal_re, double[] signal_im, int windowSize, int overlap) { if (!isPowerOfTwo(windowSize)) { throw new IllegalArgumentException("windowSize is not a power of two"); } int stepSize = windowSize - overlap; - int totalFrames = (signal.length - overlap + stepSize - 1) / stepSize; + int totalFrames = (signal_re.length - overlap + stepSize - 1) / stepSize; int out_len = totalFrames * windowSize; // Initialize the STFT output array: [time frame][frequency bin][real & imaginary parts] @@ -70,7 +71,8 @@ public static MatrixBlock[] stft(double[] signal, int windowSize, int overlap) { for (int i = 0; i < totalFrames; i++) { for (int j = 0; j < windowSize; j++) { - stftOutput_re[i * windowSize + j] = ((i * stepSize + j) < signal.length) ? signal[i * stepSize + j] : 0; + stftOutput_re[i * windowSize + j] = ((i * stepSize + j) < signal_re.length) ? signal_re[i * stepSize + j] : 0; + stftOutput_im[i * windowSize + j] = ((i * stepSize + j) < signal_im.length) ? signal_im[i * stepSize + j] : 0; } } @@ -83,18 +85,18 @@ public static MatrixBlock[] stft(double[] signal, int windowSize, int overlap) { } /** - * Function to perform STFT on two given matrices with windowSize and overlap. - * The first one represents the real values and the second one the imaginary values. - * The output also contains one matrix for the real and one for the imaginary values. + * Function to perform STFT on a given matrices with windowSize and overlap. + * The matrix represents the real values. + * The output contains one matrix for the real and one for the imaginary values. * The results of the fourier transformations are appended to each other in the output. * - * @param in matrix object representing the real values + * @param re matrix object representing the real values * @param windowSize size of window * @param overlap size of overlap * @return array of two matrix blocks */ - public static MatrixBlock[] stft(MatrixBlock[] in, int windowSize, int overlap){ - return stft(in[0].getDenseBlockValues(), windowSize, overlap); + public static MatrixBlock[] stft(MatrixBlock re, int windowSize, int overlap){ + return stft(re.getDenseBlockValues(), new double[re.getDenseBlockValues().length], windowSize, overlap); } } diff --git a/src/test/java/org/apache/sysds/test/component/matrix/STFTTest.java b/src/test/java/org/apache/sysds/test/component/matrix/STFTTest.java index aebe7c31458..4fafe57f1d2 100644 --- a/src/test/java/org/apache/sysds/test/component/matrix/STFTTest.java +++ b/src/test/java/org/apache/sysds/test/component/matrix/STFTTest.java @@ -27,116 +27,77 @@ public class STFTTest { - @Test - public void simple_test() { + @Test + public void simple_test() { - double[] signal = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; - int windowSize = 4; - int overlap = 2; - MatrixBlock[] res = stft(signal, windowSize, overlap); - double[] res_re = res[0].getDenseBlockValues(); - double[] res_im = res[1].getDenseBlockValues(); + MatrixBlock re = new MatrixBlock(1, 16, new double[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}); + MatrixBlock[] res = stft(re, 4, 2); - double[]expected_re = {6, -2, -2, -2, 14, -2, -2, -2, 22, -2, -2, -2, 30, -2, -2, -2, 38, -2, -2, -2, 46, -2, -2, -2, 54, -2, -2, -2}; + double[] res_re = res[0].getDenseBlockValues(); + double[] res_im = res[1].getDenseBlockValues(); - double[]expected_im = {0, 2, 0, -2, 0, 2, 0, -2, 0, 2, 0, -2, 0, 2, 0, -2, 0, 2, 0, -2, 0, 2, 0, -2, 0, 2, 0, -2}; + double[] expected_re = {6, -2, -2, -2, 14, -2, -2, -2, 22, -2, -2, -2, 30, -2, -2, -2, 38, -2, -2, -2, 46, -2, -2, -2, 54, -2, -2, -2}; + double[] expected_im = {0, 2, 0, -2, 0, 2, 0, -2, 0, 2, 0, -2, 0, 2, 0, -2, 0, 2, 0, -2, 0, 2, 0, -2, 0, 2, 0, -2}; + assertArrayEquals(expected_re, res_re, 0.0001); + assertArrayEquals(expected_im, res_im, 0.0001); - for(double elem : res_re){ - System.out.print(elem + " "); - } + } - for(double elem : res_re){ - System.out.print(elem+" "); - } - System.out.println(); - for(double elem : res_im){ - System.out.print(elem+" "); - } + @Test + public void matrix_block_one_dim_test(){ + MatrixBlock re = new MatrixBlock(1, 4, new double[]{0, 18, -15, 3}); - assertArrayEquals(expected_re, res_re, 0.0001); - assertArrayEquals(expected_im, res_im, 0.0001); + MatrixBlock[] res = stft(re, 4, 0); - } + double[] res_re = res[0].getDenseBlockValues(); + double[] res_im = res[1].getDenseBlockValues(); - @Test - public void matrix_block_one_dim_test(){ + double[] expected_re = {6, 15, -36, 15}; + double[] expected_im = {0, -15, 0, 15}; - double[] in = {0, 18, -15, 3}; + assertArrayEquals(expected_re, res_re, 0.0001); + assertArrayEquals(expected_im, res_im, 0.0001); - double[] expected_re = {6, 15, -36, 15}; - double[] expected_im = {0, -15, 0, 15}; + } - MatrixBlock[] res = stft(in, 4, 0); - double[] res_re = res[0].getDenseBlockValues(); - double[] res_im = res[1].getDenseBlockValues(); + @Test + public void matrix_block_one_dim_test2(){ - for(double elem : res_re){ - System.out.print(elem+" "); - } - System.out.println(); - for(double elem : res_im){ - System.out.print(elem+" "); - } + MatrixBlock re = new MatrixBlock(1, 8, new double[]{10, 5, -3, 8, 15, -6, 2, 0}); - assertArrayEquals(expected_re, res_re, 0.0001); - assertArrayEquals(expected_im, res_im, 0.0001); + MatrixBlock[] res = stft(re, 4, 2); - } + double[] res_re = res[0].getDenseBlockValues(); + double[] res_im = res[1].getDenseBlockValues(); - @Test - public void matrix_block_one_dim_test2(){ + double[] expected_re = {20.0, 13.0, -6.0, 13.0, 14.0, -18.0, 10.0, -18.0, 11.0, 13.0, 23.0, 13.0}; + double[] expected_im = {0.0, 3.0, 0.0, -3.0, 0.0, -14.0, 0.0, 14.0, 0.0, 6.0, 0.0, -6.0}; - double[] in = {10, 5, -3, 8, 15, -6, 2, 0}; + assertArrayEquals(expected_re, res_re, 0.0001); + assertArrayEquals(expected_im, res_im, 0.0001); - double[] expected_re = {20.0, 13.0, -6.0, 13.0, 14.0, -18.0, 10.0, -18.0, 11.0, 13.0, 23.0, 13.0}; - double[] expected_im = {0.0, 3.0, 0.0, -3.0, 0.0, -14.0, 0.0, 14.0, 0.0, 6.0, 0.0, -6.0}; + } - MatrixBlock[] res = stft(in, 4, 2); - double[] res_re = res[0].getDenseBlockValues(); - double[] res_im = res[1].getDenseBlockValues(); + @Test + public void matrix_block_one_dim_test3(){ - for(double elem : res_re){ - System.out.print(elem+" "); - } - System.out.println(); - for(double elem : res_im){ - System.out.print(elem+" "); - } + MatrixBlock re = new MatrixBlock(1, 8, new double[]{10, 5, -3, 8, 15, -6, 2, 0}); + MatrixBlock im = new MatrixBlock(1, 8, new double[8]); - assertArrayEquals(expected_re, res_re, 0.0001); - assertArrayEquals(expected_im, res_im, 0.0001); + MatrixBlock[] res = stft(re, im, 4, 2); - } + double[] res_re = res[0].getDenseBlockValues(); + double[] res_im = res[1].getDenseBlockValues(); - @Test - public void matrix_block_one_dim_test3(){ + double[] expected_re = {20.0, 13.0, -6.0, 13.0, 14.0, -18.0, 10.0, -18.0, 11.0, 13.0, 23.0, 13.0}; + double[] expected_im = {0.0, 3.0, 0.0, -3.0, 0.0, -14.0, 0.0, 14.0, 0.0, 6.0, 0.0, -6.0}; - //double[] in = {10, 5, -3, 8, 15, -6, 2, 0}; + assertArrayEquals(expected_re, res_re, 0.0001); + assertArrayEquals(expected_im, res_im, 0.0001); - MatrixBlock re = new MatrixBlock(1, 8, new double[]{10, 5, -3, 8, 15, -6, 2, 0}); - MatrixBlock im = new MatrixBlock(1, 8, new double[8]); - - double[] expected_re = {20.0, 13.0, -6.0, 13.0, 14.0, -18.0, 10.0, -18.0, 11.0, 13.0, 23.0, 13.0}; - double[] expected_im = {0.0, 3.0, 0.0, -3.0, 0.0, -14.0, 0.0, 14.0, 0.0, 6.0, 0.0, -6.0}; - - MatrixBlock[] res = stft(re, im, 4, 2); - double[] res_re = res[0].getDenseBlockValues(); - double[] res_im = res[1].getDenseBlockValues(); - - for(double elem : res_re){ - System.out.print(elem+" "); - } - System.out.println(); - for(double elem : res_im){ - System.out.print(elem+" "); - } - - assertArrayEquals(expected_re, res_re, 0.0001); - assertArrayEquals(expected_im, res_im, 0.0001); - - } + } } From 76103b17067634f6f6c61f14e0b45798823d5145 Mon Sep 17 00:00:00 2001 From: Jessica Priebe Date: Mon, 22 Jan 2024 13:53:32 +0100 Subject: [PATCH 080/133] stft complex values --- .../org/apache/sysds/runtime/matrix/data/LibMatrixSTFT.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixSTFT.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixSTFT.java index 07792222d53..601cd4ac9ea 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixSTFT.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixSTFT.java @@ -58,6 +58,10 @@ public static MatrixBlock[] stft(double[] signal_re, double[] signal_im, int win throw new IllegalArgumentException("windowSize is not a power of two"); } + if (signal_re.length != signal_im.length) { + throw new IllegalArgumentException("different number of real and imaginary values"); + } + int stepSize = windowSize - overlap; int totalFrames = (signal_re.length - overlap + stepSize - 1) / stepSize; int out_len = totalFrames * windowSize; From cbd26b4c13c757dc5a8a85a75ab27a34717874c2 Mon Sep 17 00:00:00 2001 From: mufwan Date: Mon, 12 Feb 2024 17:36:38 +0100 Subject: [PATCH 081/133] stft dml --- dml_test/fft.dml | 5 + dml_test/hello.dml | 1 + .../org/apache/sysds/common/Builtins.java | 1 + .../org/apache/sysds/hops/FunctionOp.java | 9 ++ .../parser/BuiltinFunctionExpression.java | 31 +++++ .../apache/sysds/parser/DMLTranslator.java | 1 + .../instructions/CPInstructionParser.java | 1 + .../cp/MultiReturnBuiltinCPInstruction.java | 8 ++ .../runtime/matrix/data/LibCommonsMath.java | 128 +++++++++++------- .../scripts/functions/unary/matrix/fft.dml | 3 + 10 files changed, 139 insertions(+), 49 deletions(-) create mode 100644 dml_test/fft.dml create mode 100644 dml_test/hello.dml create mode 100644 src/test/scripts/functions/unary/matrix/fft.dml diff --git a/dml_test/fft.dml b/dml_test/fft.dml new file mode 100644 index 00000000000..d5186897d09 --- /dev/null +++ b/dml_test/fft.dml @@ -0,0 +1,5 @@ +print("hello") +M = matrix("0 18 -15 3", rows=1, cols=4) +[r,i] = fft(M) +print(toString(r)) +print(toString(i)) \ No newline at end of file diff --git a/dml_test/hello.dml b/dml_test/hello.dml new file mode 100644 index 00000000000..dfe80e8de72 --- /dev/null +++ b/dml_test/hello.dml @@ -0,0 +1 @@ +print("Hello World"); \ No newline at end of file diff --git a/src/main/java/org/apache/sysds/common/Builtins.java b/src/main/java/org/apache/sysds/common/Builtins.java index e331e741c3c..1b18847f4a9 100644 --- a/src/main/java/org/apache/sysds/common/Builtins.java +++ b/src/main/java/org/apache/sysds/common/Builtins.java @@ -309,6 +309,7 @@ public enum Builtins { STATSNA("statsNA", true), STRATSTATS("stratstats", true), STEPLM("steplm",true, ReturnType.MULTI_RETURN), + STFT("stft", false, ReturnType.MULTI_RETURN), SQRT("sqrt", false), SUM("sum", false), SVD("svd", false, ReturnType.MULTI_RETURN), diff --git a/src/main/java/org/apache/sysds/hops/FunctionOp.java b/src/main/java/org/apache/sysds/hops/FunctionOp.java index 217b0554571..21551d2e842 100644 --- a/src/main/java/org/apache/sysds/hops/FunctionOp.java +++ b/src/main/java/org/apache/sysds/hops/FunctionOp.java @@ -221,6 +221,11 @@ else if ( getFunctionName().equalsIgnoreCase("ifft_linearized") ) { long outputIm = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(1).getDim1(), getOutputs().get(1).getDim2(), 1.0); return outputRe+outputIm; } + else if ( getFunctionName().equalsIgnoreCase("stft") ) { + long outputRe = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(0).getDim1(), getOutputs().get(0).getDim2(), 1.0); + long outputIm = OptimizerUtils.estimateSizeExactSparsity(getOutputs().get(1).getDim1(), getOutputs().get(1).getDim2(), 1.0); + return outputRe+outputIm; + } else if ( getFunctionName().equalsIgnoreCase("lstm") || getFunctionName().equalsIgnoreCase("lstm_backward") ) { // TODO: To allow for initial version to always run on the GPU return 0; @@ -286,6 +291,10 @@ else if ( getFunctionName().equalsIgnoreCase("ifft_linearized") ) { // 2 matrices of size same as the input return 2*OptimizerUtils.estimateSizeExactSparsity(getInput().get(0).getDim1(), getInput().get(0).getDim2(), 1.0); } + else if ( getFunctionName().equalsIgnoreCase("stft") ) { + // 2 matrices of size same as the input + return 2*OptimizerUtils.estimateSizeExactSparsity(getInput().get(0).getDim1(), getInput().get(0).getDim2(), 1.0); + } else if (getFunctionName().equalsIgnoreCase("batch_norm2d") || getFunctionName().equalsIgnoreCase("batch_norm2d_backward") || getFunctionName().equalsIgnoreCase("batch_norm2d_train") || getFunctionName().equalsIgnoreCase("batch_norm2d_test")) { return 0; diff --git a/src/main/java/org/apache/sysds/parser/BuiltinFunctionExpression.java b/src/main/java/org/apache/sysds/parser/BuiltinFunctionExpression.java index 6246e67afd3..ef4dddf5d61 100644 --- a/src/main/java/org/apache/sysds/parser/BuiltinFunctionExpression.java +++ b/src/main/java/org/apache/sysds/parser/BuiltinFunctionExpression.java @@ -509,6 +509,37 @@ else if(expressionOne != null){ break; } + case STFT: + { + checkNumParameters(3); + checkMatrixParam(getFirstExpr()); + + // setup output properties + DataIdentifier stftOut1 = (DataIdentifier) getOutputs()[0]; + DataIdentifier stftOut2 = (DataIdentifier) getOutputs()[1]; + + // if (getFirstExpr().getOutput().getDim2() != 1 || getFirstExpr().getOutput().getDim2() != 2) { + // raiseValidateError("Eigen Decomposition can only be done on a square matrix. Input matrix is rectangular (rows=" + getFirstExpr().getOutput().getDim1() + ", cols="+ getFirstExpr().getOutput().getDim2() +")", conditional); + // } + + checkMatrixParam(getSecondExpr()); + checkMatrixParam(getThirdExpr()); + + // Output1 - stft Values + stftOut1.setDataType(DataType.MATRIX); + stftOut1.setValueType(ValueType.FP64); + stftOut1.setDimensions(getFirstExpr().getOutput().getDim1(), getFirstExpr().getOutput().getDim2()); + stftOut1.setBlocksize(getFirstExpr().getOutput().getBlocksize()); + + // Output2 - stft Vectors + stftOut2.setDataType(DataType.MATRIX); + stftOut2.setValueType(ValueType.FP64); + stftOut2.setDimensions(getFirstExpr().getOutput().getDim1(), getFirstExpr().getOutput().getDim2()); + stftOut2.setBlocksize(getFirstExpr().getOutput().getBlocksize()); + + break; + + } case REMOVE: { checkNumParameters(2); checkListParam(getFirstExpr()); diff --git a/src/main/java/org/apache/sysds/parser/DMLTranslator.java b/src/main/java/org/apache/sysds/parser/DMLTranslator.java index 47a4be8f71c..bda1b0498b1 100644 --- a/src/main/java/org/apache/sysds/parser/DMLTranslator.java +++ b/src/main/java/org/apache/sysds/parser/DMLTranslator.java @@ -2241,6 +2241,7 @@ private Hop processMultipleReturnBuiltinFunctionExpression(BuiltinFunctionExpres case IFFT: case FFT_LINEARIZED: case IFFT_LINEARIZED: + case STFT: case LSTM: case LSTM_BACKWARD: case BATCH_NORM2D: diff --git a/src/main/java/org/apache/sysds/runtime/instructions/CPInstructionParser.java b/src/main/java/org/apache/sysds/runtime/instructions/CPInstructionParser.java index 81554bae04c..cad494b82ba 100644 --- a/src/main/java/org/apache/sysds/runtime/instructions/CPInstructionParser.java +++ b/src/main/java/org/apache/sysds/runtime/instructions/CPInstructionParser.java @@ -336,6 +336,7 @@ public class CPInstructionParser extends InstructionParser { String2CPInstructionType.put("ifft", CPType.MultiReturnComplexMatrixBuiltin); String2CPInstructionType.put("fft_linearized", CPType.MultiReturnBuiltin); String2CPInstructionType.put("ifft_linearized", CPType.MultiReturnComplexMatrixBuiltin); + String2CPInstructionType.put( "stft", CPType.MultiReturnBuiltin); String2CPInstructionType.put( "svd", CPType.MultiReturnBuiltin); String2CPInstructionType.put( "partition", CPType.Partition); diff --git a/src/main/java/org/apache/sysds/runtime/instructions/cp/MultiReturnBuiltinCPInstruction.java b/src/main/java/org/apache/sysds/runtime/instructions/cp/MultiReturnBuiltinCPInstruction.java index 4719948ec01..a65b56c8ace 100644 --- a/src/main/java/org/apache/sysds/runtime/instructions/cp/MultiReturnBuiltinCPInstruction.java +++ b/src/main/java/org/apache/sysds/runtime/instructions/cp/MultiReturnBuiltinCPInstruction.java @@ -124,6 +124,14 @@ else if(parts.length == 3 && opcode.equalsIgnoreCase("fft_linearized")) { return new MultiReturnBuiltinCPInstruction(null, null, outputs, opcode, str); } + else if ( opcode.equalsIgnoreCase("stft") ) { + // one input and two outputs + CPOperand in1 = new CPOperand(parts[1]); + outputs.add ( new CPOperand(parts[2], ValueType.FP64, DataType.MATRIX) ); + outputs.add ( new CPOperand(parts[3], ValueType.FP64, DataType.MATRIX) ); + + return new MultiReturnBuiltinCPInstruction(null, in1, outputs, opcode, str); + } else if ( opcode.equalsIgnoreCase("svd") ) { CPOperand in1 = new CPOperand(parts[1]); diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java index 6e7fa816fd0..4f7a353de84 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java @@ -6,9 +6,9 @@ * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at - * + * * http://www.apache.org/licenses/LICENSE-2.0 - * + * * Unless required by applicable law or agreed to in writing, * software distributed under the License is distributed on an * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY @@ -53,15 +53,16 @@ import static org.apache.sysds.runtime.matrix.data.LibMatrixFourier.ifft; import static org.apache.sysds.runtime.matrix.data.LibMatrixFourier.fft_linearized; import static org.apache.sysds.runtime.matrix.data.LibMatrixFourier.ifft_linearized; +import static org.apache.sysds.runtime.matrix.data.LibMatrixSTFT.stft; /** - * Library for matrix operations that need invocation of - * Apache Commons Math library. - * + * Library for matrix operations that need invocation of + * Apache Commons Math library. + * * This library currently supports following operations: - * matrix inverse, matrix decompositions (QR, LU, Eigen), solve + * matrix inverse, matrix decompositions (QR, LU, Eigen), solve */ -public class LibCommonsMath +public class LibCommonsMath { private static final Log LOG = LogFactory.getLog(LibCommonsMath.class.getName()); private static final double RELATIVE_SYMMETRY_THRESHOLD = 1e-6; @@ -70,11 +71,11 @@ public class LibCommonsMath private LibCommonsMath() { //prevent instantiation via private constructor } - + public static boolean isSupportedUnaryOperation( String opcode ) { return ( opcode.equals("inverse") || opcode.equals("cholesky") ); } - + public static boolean isSupportedMultiReturnOperation( String opcode ) { switch (opcode) { @@ -85,16 +86,17 @@ public static boolean isSupportedMultiReturnOperation( String opcode ) { case "ifft": case "fft_linearized": case "ifft_linearized": + case "stft": case "svd": return true; default: return false; } } - + public static boolean isSupportedMatrixMatrixOperation( String opcode ) { return ( opcode.equals("solve") ); } - + public static MatrixBlock unaryOperations(MatrixBlock inj, String opcode) { Array2DRowRealMatrix matrixInput = DataConverter.convertToArray2DRowRealMatrix(inj); if(opcode.equals("inverse")) @@ -119,6 +121,10 @@ public static MatrixBlock[] multiReturnOperations(MatrixBlock in, String opcode, return multiReturnOperations(in, opcode, threads, 1); } + public static MatrixBlock[] multiReturnOperations(MatrixBlock in, String opcode, int windowSize, int overlap) { + return computeSTFT(in, null, windowSize, overlap); + } + public static MatrixBlock[] multiReturnOperations(MatrixBlock in, String opcode, int threads, long seed) { if(opcode.equals("qr")) return computeQR(in); @@ -167,10 +173,10 @@ public static MatrixBlock matrixMatrixOperations(MatrixBlock in1, MatrixBlock in } return null; } - + /** * Function to solve a given system of equations. - * + * * @param in1 matrix object 1 * @param in2 matrix object 2 * @return matrix block @@ -180,62 +186,62 @@ private static MatrixBlock computeSolve(MatrixBlock in1, MatrixBlock in2) { //to avoid unnecessary conversion as QR internally creates a BlockRealMatrix BlockRealMatrix matrixInput = DataConverter.convertToBlockRealMatrix(in1); BlockRealMatrix vectorInput = DataConverter.convertToBlockRealMatrix(in2); - + /*LUDecompositionImpl ludecompose = new LUDecompositionImpl(matrixInput); DecompositionSolver lusolver = ludecompose.getSolver(); RealMatrix solutionMatrix = lusolver.solve(vectorInput);*/ - + // Setup a solver based on QR Decomposition QRDecomposition qrdecompose = new QRDecomposition(matrixInput); DecompositionSolver solver = qrdecompose.getSolver(); // Invoke solve RealMatrix solutionMatrix = solver.solve(vectorInput); - + return DataConverter.convertToMatrixBlock(solutionMatrix); } - + /** * Function to perform QR decomposition on a given matrix. - * + * * @param in matrix object * @return array of matrix blocks */ private static MatrixBlock[] computeQR(MatrixBlock in) { Array2DRowRealMatrix matrixInput = DataConverter.convertToArray2DRowRealMatrix(in); - + // Perform QR decomposition QRDecomposition qrdecompose = new QRDecomposition(matrixInput); RealMatrix H = qrdecompose.getH(); RealMatrix R = qrdecompose.getR(); - + // Read the results into native format MatrixBlock mbH = DataConverter.convertToMatrixBlock(H.getData()); MatrixBlock mbR = DataConverter.convertToMatrixBlock(R.getData()); return new MatrixBlock[] { mbH, mbR }; } - + /** * Function to perform LU decomposition on a given matrix. - * + * * @param in matrix object * @return array of matrix blocks */ private static MatrixBlock[] computeLU(MatrixBlock in) { if(in.getNumRows() != in.getNumColumns()) { throw new DMLRuntimeException( - "LU Decomposition can only be done on a square matrix. Input matrix is rectangular (rows=" - + in.getNumRows() + ", cols=" + in.getNumColumns() + ")"); + "LU Decomposition can only be done on a square matrix. Input matrix is rectangular (rows=" + + in.getNumRows() + ", cols=" + in.getNumColumns() + ")"); } - + Array2DRowRealMatrix matrixInput = DataConverter.convertToArray2DRowRealMatrix(in); - + // Perform LUP decomposition LUDecomposition ludecompose = new LUDecomposition(matrixInput); RealMatrix P = ludecompose.getP(); RealMatrix L = ludecompose.getL(); RealMatrix U = ludecompose.getU(); - + // Read the results into native format MatrixBlock mbP = DataConverter.convertToMatrixBlock(P.getData()); MatrixBlock mbL = DataConverter.convertToMatrixBlock(L.getData()); @@ -243,20 +249,20 @@ private static MatrixBlock[] computeLU(MatrixBlock in) { return new MatrixBlock[] { mbP, mbL, mbU }; } - + /** * Function to perform Eigen decomposition on a given matrix. * Input must be a symmetric matrix. - * + * * @param in matrix object * @return array of matrix blocks */ private static MatrixBlock[] computeEigen(MatrixBlock in) { if ( in.getNumRows() != in.getNumColumns() ) { throw new DMLRuntimeException("Eigen Decomposition can only be done on a square matrix. " - + "Input matrix is rectangular (rows=" + in.getNumRows() + ", cols="+ in.getNumColumns() +")"); + + "Input matrix is rectangular (rows=" + in.getNumRows() + ", cols="+ in.getNumColumns() +")"); } - + EigenDecomposition eigendecompose = null; try { Array2DRowRealMatrix matrixInput = DataConverter.convertToArray2DRowRealMatrix(in); @@ -266,7 +272,7 @@ private static MatrixBlock[] computeEigen(MatrixBlock in) { LOG.warn("Eigen: "+ ex.getMessage()+". Falling back to regularized eigen factorization."); eigendecompose = computeEigenRegularized(in); } - + RealMatrix eVectorsMatrix = eigendecompose.getV(); double[][] eVectors = eVectorsMatrix.getData(); double[] eValues = eigendecompose.getRealEigenvalues(); @@ -277,7 +283,7 @@ private static MatrixBlock[] computeEigen(MatrixBlock in) { private static EigenDecomposition computeEigenRegularized(MatrixBlock in) { if( in == null || in.isEmptyBlock(false) ) throw new DMLRuntimeException("Invalid empty block"); - + //slightly modify input for regularization (pos/neg) MatrixBlock in2 = new MatrixBlock(in, false); DenseBlock a = in2.getDenseBlock(); @@ -289,10 +295,10 @@ private static EigenDecomposition computeEigenRegularized(MatrixBlock in) { avals[apos+j] += Math.signum(v) * EIGEN_LAMBDA; } } - + //run eigen decomposition return new EigenDecomposition( - DataConverter.convertToArray2DRowRealMatrix(in2)); + DataConverter.convertToArray2DRowRealMatrix(in2)); } /** @@ -409,6 +415,30 @@ private static MatrixBlock[] computeIFFT_LINEARIZED(MatrixBlock re, MatrixBlock private static MatrixBlock[] computeIFFT_LINEARIZED(MatrixBlock re, int threads){ return computeIFFT_LINEARIZED(re, null, threads); } + + /** + * Function to perform STFT on a given matrix. + * + * @param in1 matrix object + * @param in2 matrix object + * @param windowSize of stft + * @param overlap of stft + * @return array of matrix blocks + */ + private static MatrixBlock[] computeSTFT(MatrixBlock in1, MatrixBlock in2, int windowSize, int overlap) { + if (in1 == null || in1.isEmptyBlock(false)) + throw new DMLRuntimeException("Invalid empty block"); + + // run stft + if (in2 != null) { + in1.sparseToDense(); + in2.sparseToDense(); + return stft(in1, in2, windowSize, overlap); + } else { + in1.sparseToDense(); + return stft(in1, windowSize, overlap); + } + /** * Performs Singular Value Decomposition. Calls Apache Commons Math SVD. @@ -416,7 +446,7 @@ private static MatrixBlock[] computeIFFT_LINEARIZED(MatrixBlock re, int threads) * U is the left singular matrix, Sigma is the singular values matrix returned as a * column matrix and Vt is the transpose of the right singular matrix V. * However, the returned array has { U, Sigma, V} - * + * * @param in Input matrix * @return An array containing U, Sigma & V */ @@ -434,17 +464,17 @@ private static MatrixBlock[] computeSvd(MatrixBlock in) { return new MatrixBlock[] { U, Sigma, V }; } - + /** * Function to compute matrix inverse via matrix decomposition. - * + * * @param in commons-math3 Array2DRowRealMatrix * @return matrix block */ private static MatrixBlock computeMatrixInverse(Array2DRowRealMatrix in) { if(!in.isSquare()) throw new DMLRuntimeException("Input to inv() must be square matrix -- given: a " + in.getRowDimension() - + "x" + in.getColumnDimension() + " matrix."); + + "x" + in.getColumnDimension() + " matrix."); QRDecomposition qrdecompose = new QRDecomposition(in); DecompositionSolver solver = qrdecompose.getSolver(); @@ -454,18 +484,18 @@ private static MatrixBlock computeMatrixInverse(Array2DRowRealMatrix in) { } /** - * Function to compute Cholesky decomposition of the given input matrix. + * Function to compute Cholesky decomposition of the given input matrix. * The input must be a real symmetric positive-definite matrix. - * + * * @param in commons-math3 Array2DRowRealMatrix * @return matrix block */ private static MatrixBlock computeCholesky(Array2DRowRealMatrix in) { if(!in.isSquare()) throw new DMLRuntimeException("Input to cholesky() must be square matrix -- given: a " - + in.getRowDimension() + "x" + in.getColumnDimension() + " matrix."); + + in.getRowDimension() + "x" + in.getColumnDimension() + " matrix."); CholeskyDecomposition cholesky = new CholeskyDecomposition(in, RELATIVE_SYMMETRY_THRESHOLD, - CholeskyDecomposition.DEFAULT_ABSOLUTE_POSITIVITY_THRESHOLD); + CholeskyDecomposition.DEFAULT_ABSOLUTE_POSITIVITY_THRESHOLD); RealMatrix rmL = cholesky.getL(); return DataConverter.convertToMatrixBlock(rmL.getData()); } @@ -506,8 +536,8 @@ private static MatrixBlock randNormalizedVect(int dim, int threads, long seed) { private static MatrixBlock[] computeEigenLanczos(MatrixBlock in, int threads, long seed) { if(in.getNumRows() != in.getNumColumns()) { throw new DMLRuntimeException( - "Lanczos algorithm and Eigen Decomposition can only be done on a square matrix. " - + "Input matrix is rectangular (rows=" + in.getNumRows() + ", cols=" + in.getNumColumns() + ")"); + "Lanczos algorithm and Eigen Decomposition can only be done on a square matrix. " + + "Input matrix is rectangular (rows=" + in.getNumRows() + ", cols=" + in.getNumColumns() + ")"); } int m = in.getNumRows(); @@ -560,7 +590,7 @@ private static MatrixBlock[] computeEigenLanczos(MatrixBlock in, int threads, lo private static MatrixBlock[] computeQR2(MatrixBlock in, int threads) { if(in.getNumRows() != in.getNumColumns()) { throw new DMLRuntimeException("QR2 Decomposition can only be done on a square matrix. " - + "Input matrix is rectangular (rows=" + in.getNumRows() + ", cols=" + in.getNumColumns() + ")"); + + "Input matrix is rectangular (rows=" + in.getNumRows() + ", cols=" + in.getNumColumns() + ")"); } int m = in.rlen; @@ -617,7 +647,7 @@ private static MatrixBlock[] computeEigenQR(MatrixBlock in, int threads) { private static MatrixBlock[] computeEigenQR(MatrixBlock in, int num_iterations, double tol, int threads) { if(in.getNumRows() != in.getNumColumns()) { throw new DMLRuntimeException("Eigen Decomposition (QR) can only be done on a square matrix. " - + "Input matrix is rectangular (rows=" + in.getNumRows() + ", cols=" + in.getNumColumns() + ")"); + + "Input matrix is rectangular (rows=" + in.getNumRows() + ", cols=" + in.getNumColumns() + ")"); } int m = in.rlen; @@ -640,7 +670,7 @@ private static MatrixBlock[] computeEigenQR(MatrixBlock in, int num_iterations, double[] eval = new double[m]; for(int i = 0; i < m; i++) eval[i] = check[i*m+i]; - + double[] evec = Q_prod.getDenseBlockValues(); return sortEVs(eval, evec); } @@ -755,4 +785,4 @@ private static MatrixBlock[] sortEVs(double[] eValues, double[] eVectors) { evec.init(eVectors, n, n); return new MatrixBlock[] {eval, evec}; } -} +} \ No newline at end of file diff --git a/src/test/scripts/functions/unary/matrix/fft.dml b/src/test/scripts/functions/unary/matrix/fft.dml new file mode 100644 index 00000000000..9350001cb6d --- /dev/null +++ b/src/test/scripts/functions/unary/matrix/fft.dml @@ -0,0 +1,3 @@ + +print("hello") M = matrix("0 18 -15 3", rows=1, cols=4) [r,i] = fft(M) +print(toString(r)) print(toString(i)) \ No newline at end of file From 5fc3c868b48bb9dcd34810f63d7cbc92de768770 Mon Sep 17 00:00:00 2001 From: mufwan Date: Mon, 12 Feb 2024 17:37:56 +0100 Subject: [PATCH 082/133] stft dml --- src/test/scripts/functions/unary/matrix/fft.dml | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 src/test/scripts/functions/unary/matrix/fft.dml diff --git a/src/test/scripts/functions/unary/matrix/fft.dml b/src/test/scripts/functions/unary/matrix/fft.dml deleted file mode 100644 index 9350001cb6d..00000000000 --- a/src/test/scripts/functions/unary/matrix/fft.dml +++ /dev/null @@ -1,3 +0,0 @@ - -print("hello") M = matrix("0 18 -15 3", rows=1, cols=4) [r,i] = fft(M) -print(toString(r)) print(toString(i)) \ No newline at end of file From ce6239f4df5da91684d184ddb89ec65bb56113b9 Mon Sep 17 00:00:00 2001 From: mufwan Date: Tue, 13 Feb 2024 01:30:39 +0100 Subject: [PATCH 083/133] stft dml integration --- dml_test/stft.dml | 6 ++ fft.dml | 5 ++ .../parser/BuiltinFunctionExpression.java | 5 +- .../instructions/CPInstructionParser.java | 1 + .../cp/ComputationCPInstruction.java | 20 +++++- .../cp/MultiReturnBuiltinCPInstruction.java | 3 +- ...turnComplexMatrixBuiltinCPInstruction.java | 63 ++++++++++++++++++- .../runtime/matrix/data/LibCommonsMath.java | 33 +++++++++- .../runtime/matrix/data/LibMatrixSTFT.java | 3 + .../sysds/test/component/matrix/STFTTest.java | 2 +- 10 files changed, 130 insertions(+), 11 deletions(-) create mode 100644 dml_test/stft.dml create mode 100644 fft.dml diff --git a/dml_test/stft.dml b/dml_test/stft.dml new file mode 100644 index 00000000000..d42050000cb --- /dev/null +++ b/dml_test/stft.dml @@ -0,0 +1,6 @@ +print("hello") +M = matrix("0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15", rows=1, cols=16) +[r,i] = stft(M, 4, 2) +print(toString(r)) +print(toString(i)) + diff --git a/fft.dml b/fft.dml new file mode 100644 index 00000000000..d5186897d09 --- /dev/null +++ b/fft.dml @@ -0,0 +1,5 @@ +print("hello") +M = matrix("0 18 -15 3", rows=1, cols=4) +[r,i] = fft(M) +print(toString(r)) +print(toString(i)) \ No newline at end of file diff --git a/src/main/java/org/apache/sysds/parser/BuiltinFunctionExpression.java b/src/main/java/org/apache/sysds/parser/BuiltinFunctionExpression.java index ef4dddf5d61..87c51a6e1ba 100644 --- a/src/main/java/org/apache/sysds/parser/BuiltinFunctionExpression.java +++ b/src/main/java/org/apache/sysds/parser/BuiltinFunctionExpression.java @@ -511,7 +511,6 @@ else if(expressionOne != null){ } case STFT: { - checkNumParameters(3); checkMatrixParam(getFirstExpr()); // setup output properties @@ -522,8 +521,8 @@ else if(expressionOne != null){ // raiseValidateError("Eigen Decomposition can only be done on a square matrix. Input matrix is rectangular (rows=" + getFirstExpr().getOutput().getDim1() + ", cols="+ getFirstExpr().getOutput().getDim2() +")", conditional); // } - checkMatrixParam(getSecondExpr()); - checkMatrixParam(getThirdExpr()); + //checkMatrixParam(getSecondExpr()); + //checkMatrixParam(getThirdExpr()); // Output1 - stft Values stftOut1.setDataType(DataType.MATRIX); diff --git a/src/main/java/org/apache/sysds/runtime/instructions/CPInstructionParser.java b/src/main/java/org/apache/sysds/runtime/instructions/CPInstructionParser.java index cad494b82ba..91c302b4ea1 100644 --- a/src/main/java/org/apache/sysds/runtime/instructions/CPInstructionParser.java +++ b/src/main/java/org/apache/sysds/runtime/instructions/CPInstructionParser.java @@ -59,6 +59,7 @@ import org.apache.sysds.runtime.instructions.cp.MultiReturnBuiltinCPInstruction; import org.apache.sysds.runtime.instructions.cp.MultiReturnComplexMatrixBuiltinCPInstruction; import org.apache.sysds.runtime.instructions.cp.MultiReturnParameterizedBuiltinCPInstruction; +import org.apache.sysds.runtime.instructions.cp.MultiReturnComplexMatrixBuiltinCPInstruction; import org.apache.sysds.runtime.instructions.cp.PMMJCPInstruction; import org.apache.sysds.runtime.instructions.cp.ParameterizedBuiltinCPInstruction; import org.apache.sysds.runtime.instructions.cp.PrefetchCPInstruction; diff --git a/src/main/java/org/apache/sysds/runtime/instructions/cp/ComputationCPInstruction.java b/src/main/java/org/apache/sysds/runtime/instructions/cp/ComputationCPInstruction.java index de450369918..9c079c9c0af 100644 --- a/src/main/java/org/apache/sysds/runtime/instructions/cp/ComputationCPInstruction.java +++ b/src/main/java/org/apache/sysds/runtime/instructions/cp/ComputationCPInstruction.java @@ -35,7 +35,7 @@ public abstract class ComputationCPInstruction extends CPInstruction implements LineageTraceable { public final CPOperand output; - public final CPOperand input1, input2, input3; + public final CPOperand input1, input2, input3, input4; protected ComputationCPInstruction(CPType type, Operator op, CPOperand in1, CPOperand in2, CPOperand out, String opcode, String istr) { @@ -43,6 +43,7 @@ protected ComputationCPInstruction(CPType type, Operator op, CPOperand in1, CPOp input1 = in1; input2 = in2; input3 = null; + input4 = null; output = out; } @@ -52,10 +53,21 @@ protected ComputationCPInstruction(CPType type, Operator op, CPOperand in1, CPOp input1 = in1; input2 = in2; input3 = in3; + input4 = null; output = out; } - public String getOutputVariableName() { + protected ComputationCPInstruction(CPType type, Operator op, CPOperand in1, CPOperand in2, CPOperand out, String opcode, + String istr, CPOperand windowSize, CPOperand overlap) { + super(type, op, opcode, istr); + input1 = in1; + input2 = in2; + input3 = windowSize; + input4 = overlap; + output = out; + } + + public String getOutputVariableName() { return output.getName(); } @@ -64,7 +76,7 @@ public CPOperand getOutput(){ } public CPOperand[] getInputs(){ - return new CPOperand[]{input1, input2, input3}; + return new CPOperand[]{input1, input2, input3, input4}; } public boolean hasFrameInput() { @@ -74,6 +86,8 @@ public boolean hasFrameInput() { return true; if (input3 != null && input3.isFrame()) return true; + if (input4 != null && input4.isFrame()) + return true; return false; } diff --git a/src/main/java/org/apache/sysds/runtime/instructions/cp/MultiReturnBuiltinCPInstruction.java b/src/main/java/org/apache/sysds/runtime/instructions/cp/MultiReturnBuiltinCPInstruction.java index a65b56c8ace..0eca433090c 100644 --- a/src/main/java/org/apache/sysds/runtime/instructions/cp/MultiReturnBuiltinCPInstruction.java +++ b/src/main/java/org/apache/sysds/runtime/instructions/cp/MultiReturnBuiltinCPInstruction.java @@ -21,6 +21,7 @@ import java.util.ArrayList; import java.util.List; +import java.util.Objects; import org.apache.commons.lang3.tuple.Pair; import org.apache.sysds.common.Types.DataType; @@ -157,7 +158,7 @@ public int getNumOutputs() { public void processInstruction(ExecutionContext ec) { if(!LibCommonsMath.isSupportedMultiReturnOperation(getOpcode())) throw new DMLRuntimeException("Invalid opcode in MultiReturnBuiltin instruction: " + getOpcode()); - + MatrixBlock in = ec.getMatrixInput(input1.getName()); MatrixBlock[] out = LibCommonsMath.multiReturnOperations(in, getOpcode()); ec.releaseMatrixInput(input1.getName()); diff --git a/src/main/java/org/apache/sysds/runtime/instructions/cp/MultiReturnComplexMatrixBuiltinCPInstruction.java b/src/main/java/org/apache/sysds/runtime/instructions/cp/MultiReturnComplexMatrixBuiltinCPInstruction.java index a279ff56654..0c50fd6dc69 100644 --- a/src/main/java/org/apache/sysds/runtime/instructions/cp/MultiReturnComplexMatrixBuiltinCPInstruction.java +++ b/src/main/java/org/apache/sysds/runtime/instructions/cp/MultiReturnComplexMatrixBuiltinCPInstruction.java @@ -52,6 +52,13 @@ private MultiReturnComplexMatrixBuiltinCPInstruction(Operator op, CPOperand inpu _outputs = outputs; } + private MultiReturnComplexMatrixBuiltinCPInstruction(Operator op, CPOperand input1, CPOperand input2, ArrayList outputs, + String opcode, + String istr, CPOperand windowSize, CPOperand overlap) { + super(CPType.MultiReturnBuiltin, op, input1, input2, outputs.get(0), opcode, istr, windowSize, overlap); + _outputs = outputs; + } + public CPOperand getOutput(int i) { return _outputs.get(i); } @@ -101,6 +108,24 @@ public static MultiReturnComplexMatrixBuiltinCPInstruction parseInstruction(Stri return new MultiReturnComplexMatrixBuiltinCPInstruction(null, in1, outputs, opcode, str); } + else if (parts.length == 6 &&opcode.equalsIgnoreCase("stft")) { + CPOperand in1 = new CPOperand(parts[1]); + CPOperand windowSize = new CPOperand(parts[2]); + CPOperand overlap = new CPOperand(parts[3]); + outputs.add(new CPOperand(parts[4], ValueType.FP64, DataType.MATRIX)); + outputs.add(new CPOperand(parts[5], ValueType.FP64, DataType.MATRIX)); + + return new MultiReturnComplexMatrixBuiltinCPInstruction(null, in1, null, outputs, opcode, str, windowSize, overlap); + } else if (parts.length == 7 &&opcode.equalsIgnoreCase("stft")) { + CPOperand in1 = new CPOperand(parts[1]); + CPOperand in2 = new CPOperand(parts[2]); + CPOperand windowSize = new CPOperand(parts[3]); + CPOperand overlap = new CPOperand(parts[4]); + outputs.add(new CPOperand(parts[5], ValueType.FP64, DataType.MATRIX)); + outputs.add(new CPOperand(parts[6], ValueType.FP64, DataType.MATRIX)); + + return new MultiReturnComplexMatrixBuiltinCPInstruction(null, in1, in2, outputs, opcode, str, windowSize, overlap); + } else { throw new DMLRuntimeException("Invalid opcode in MultiReturnBuiltin instruction: " + opcode); } @@ -113,7 +138,11 @@ public int getNumOutputs() { @Override public void processInstruction(ExecutionContext ec) { - if (input2 == null) + if (getOpcode().equals("stft") && input2 == null) + processSTFTInstruction(ec); + else if (getOpcode().equals("stft")) + processSTFTTwoInstruction(ec); + else if (input2 == null) processOneInputInstruction(ec); else processTwoInputInstruction(ec); @@ -147,6 +176,38 @@ private void processTwoInputInstruction(ExecutionContext ec) { } } + private void processSTFTInstruction(ExecutionContext ec) { + if (!LibCommonsMath.isSupportedMultiReturnOperation(getOpcode())) + throw new DMLRuntimeException("Invalid opcode in MultiReturnBuiltin instruction: " + getOpcode()); + + MatrixBlock in1 = ec.getMatrixInput(input1.getName()); + //MatrixBlock in2 = ec.getMatrixInput(input2.getName()); + int windowSize = Integer.parseInt(input3.getName()); + int overlap = Integer.parseInt(input4.getName()); + MatrixBlock[] out = LibCommonsMath.multiReturnOperations(in1, getOpcode(), windowSize, overlap); + ec.releaseMatrixInput(input1.getName()); + + for (int i = 0; i < _outputs.size(); i++) { + ec.setMatrixOutput(_outputs.get(i).getName(), out[i]); + } + } + + private void processSTFTTwoInstruction(ExecutionContext ec) { + if (!LibCommonsMath.isSupportedMultiReturnOperation(getOpcode())) + throw new DMLRuntimeException("Invalid opcode in MultiReturnBuiltin instruction: " + getOpcode()); + + MatrixBlock in1 = ec.getMatrixInput(input1.getName()); + MatrixBlock in2 = ec.getMatrixInput(input2.getName()); + int windowSize = Integer.parseInt(input3.getName()); + int overlap = Integer.parseInt(input4.getName()); + MatrixBlock[] out = LibCommonsMath.multiReturnOperations(in1, in2, getOpcode(), windowSize, overlap); + ec.releaseMatrixInput(input1.getName(), input2.getName()); + + for (int i = 0; i < _outputs.size(); i++) { + ec.setMatrixOutput(_outputs.get(i).getName(), out[i]); + } + } + @Override public boolean hasSingleLineage() { return false; diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java index 4f7a353de84..d13d7f6f474 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java @@ -122,7 +122,36 @@ public static MatrixBlock[] multiReturnOperations(MatrixBlock in, String opcode, } public static MatrixBlock[] multiReturnOperations(MatrixBlock in, String opcode, int windowSize, int overlap) { - return computeSTFT(in, null, windowSize, overlap); + if(opcode.equals("stft")) + return computeSTFT(in, null, windowSize, overlap); + else if(opcode.equals("qr")) + return computeQR(in); + else if (opcode.equals("qr2")) + return computeQR2(in, windowSize); + else if (opcode.equals("lu")) + return computeLU(in); + else if (opcode.equals("eigen")) + return computeEigen(in); + else if (opcode.equals("eigen_lanczos")) + return computeEigenLanczos(in, windowSize, overlap); + else if (opcode.equals("eigen_qr")) + return computeEigenQR(in, windowSize); + else if (opcode.equals("svd")) + return computeSvd(in); + else if (opcode.equals("fft")) + return computeFFT(in); + else if (opcode.equals("ifft")) + return computeIFFT(in, null); + return null; + } + + public static MatrixBlock[] multiReturnOperations(MatrixBlock in1, MatrixBlock in2, String opcode, int windowSize, int overlap) { + if(opcode.equals("stft")) + return computeSTFT(in1, in2, windowSize, overlap); + else if(opcode.equals("ifft")) + return computeIFFT(in1, in2); + else + return null; } public static MatrixBlock[] multiReturnOperations(MatrixBlock in, String opcode, int threads, long seed) { @@ -438,7 +467,7 @@ private static MatrixBlock[] computeSTFT(MatrixBlock in1, MatrixBlock in2, int w in1.sparseToDense(); return stft(in1, windowSize, overlap); } - + } /** * Performs Singular Value Decomposition. Calls Apache Commons Math SVD. diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixSTFT.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixSTFT.java index 601cd4ac9ea..ff990129741 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixSTFT.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixSTFT.java @@ -63,6 +63,9 @@ public static MatrixBlock[] stft(double[] signal_re, double[] signal_im, int win } int stepSize = windowSize - overlap; + if (stepSize == 0) { + throw new IllegalArgumentException("windowSize - overlap is zero"); + } int totalFrames = (signal_re.length - overlap + stepSize - 1) / stepSize; int out_len = totalFrames * windowSize; diff --git a/src/test/java/org/apache/sysds/test/component/matrix/STFTTest.java b/src/test/java/org/apache/sysds/test/component/matrix/STFTTest.java index 4fafe57f1d2..5e18a87e8e2 100644 --- a/src/test/java/org/apache/sysds/test/component/matrix/STFTTest.java +++ b/src/test/java/org/apache/sysds/test/component/matrix/STFTTest.java @@ -85,7 +85,7 @@ public void matrix_block_one_dim_test2(){ public void matrix_block_one_dim_test3(){ MatrixBlock re = new MatrixBlock(1, 8, new double[]{10, 5, -3, 8, 15, -6, 2, 0}); - MatrixBlock im = new MatrixBlock(1, 8, new double[8]); + MatrixBlock im = new MatrixBlock(1, 8, new double[]{0, 0, 0, 0, 0, 0, 0, 0}); MatrixBlock[] res = stft(re, im, 4, 2); From 65043e792ff020b5223768b413d07033ce37e96f Mon Sep 17 00:00:00 2001 From: mufwan Date: Wed, 14 Feb 2024 23:39:01 +0100 Subject: [PATCH 084/133] handle zero matrix --- dml_test/stft.dml | 6 +-- .../parser/BuiltinFunctionExpression.java | 38 ++++++++++++++----- .../runtime/matrix/data/LibCommonsMath.java | 34 +++++++++++------ 3 files changed, 53 insertions(+), 25 deletions(-) diff --git a/dml_test/stft.dml b/dml_test/stft.dml index d42050000cb..425ff9d8fcd 100644 --- a/dml_test/stft.dml +++ b/dml_test/stft.dml @@ -1,6 +1,6 @@ print("hello") -M = matrix("0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15", rows=1, cols=16) -[r,i] = stft(M, 4, 2) +M = matrix("0 0 0 0 0 0", rows=1, cols=6) +I = matrix("0 0 0 0 0 0", rows=1, cols=6) +[r,i] = stft(M, I, 4, 3) print(toString(r)) print(toString(i)) - diff --git a/src/main/java/org/apache/sysds/parser/BuiltinFunctionExpression.java b/src/main/java/org/apache/sysds/parser/BuiltinFunctionExpression.java index 87c51a6e1ba..746c9c9577b 100644 --- a/src/main/java/org/apache/sysds/parser/BuiltinFunctionExpression.java +++ b/src/main/java/org/apache/sysds/parser/BuiltinFunctionExpression.java @@ -357,9 +357,8 @@ public void validateExpression(MultiAssignmentStatement stmt, HashMap 0) { + raiseValidateError("Missing argument for function " + this.getOpCode(), false, + LanguageErrorCodes.INVALID_PARAMETERS); + } else if (getFifthExpr() != null) { + raiseValidateError("Invalid number of arguments for function " + this.getOpCode().toString().toLowerCase() + + "(). This function only takes 3 or 4 arguments.", false); + } else if (_args.length == 3) { + checkScalarParam(getSecondExpr()); + checkScalarParam(getThirdExpr()); + if (!isPowerOfTwo(((ConstIdentifier) getSecondExpr().getOutput()).getLongValue())) { + raiseValidateError("This FFT implementation is only defined for matrices with dimensions that are powers of 2." + + "The window size (2nd argument) is not a power of two", false, LanguageErrorCodes.INVALID_PARAMETERS); + } + } else if (_args.length == 4) { + checkMatrixParam(getSecondExpr()); + checkScalarParam(getThirdExpr()); + checkScalarParam(getFourthExpr()); + if (!isPowerOfTwo(((ConstIdentifier) getThirdExpr().getOutput()).getLongValue())) { + raiseValidateError("This FFT implementation is only defined for matrices with dimensions that are powers of 2." + + "The window size (3rd argument) is not a power of two", false, LanguageErrorCodes.INVALID_PARAMETERS); + } else if (getFirstExpr().getOutput().getDim2() != getSecondExpr().getOutput().getDim2()) { + raiseValidateError("The real and imaginary part of the provided matrix are of different dimensions.", false); + } else if (getFirstExpr().getOutput().getDim1() != 1 || getSecondExpr().getOutput().getDim1() != 1) { + raiseValidateError("This FFT implementation is only defined for one-dimensional matrices.", false, LanguageErrorCodes.INVALID_PARAMETERS); + } + } + // setup output properties DataIdentifier stftOut1 = (DataIdentifier) getOutputs()[0]; DataIdentifier stftOut2 = (DataIdentifier) getOutputs()[1]; - // if (getFirstExpr().getOutput().getDim2() != 1 || getFirstExpr().getOutput().getDim2() != 2) { - // raiseValidateError("Eigen Decomposition can only be done on a square matrix. Input matrix is rectangular (rows=" + getFirstExpr().getOutput().getDim1() + ", cols="+ getFirstExpr().getOutput().getDim2() +")", conditional); - // } - - //checkMatrixParam(getSecondExpr()); - //checkMatrixParam(getThirdExpr()); - // Output1 - stft Values stftOut1.setDataType(DataType.MATRIX); stftOut1.setValueType(ValueType.FP64); @@ -537,7 +556,6 @@ else if(expressionOne != null){ stftOut2.setBlocksize(getFirstExpr().getOutput().getBlocksize()); break; - } case REMOVE: { checkNumParameters(2); diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java index d13d7f6f474..44c6624f8d9 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java @@ -448,24 +448,34 @@ private static MatrixBlock[] computeIFFT_LINEARIZED(MatrixBlock re, int threads) /** * Function to perform STFT on a given matrix. * - * @param in1 matrix object - * @param in2 matrix object + * @param re matrix object + * @param im matrix object * @param windowSize of stft * @param overlap of stft * @return array of matrix blocks */ - private static MatrixBlock[] computeSTFT(MatrixBlock in1, MatrixBlock in2, int windowSize, int overlap) { - if (in1 == null || in1.isEmptyBlock(false)) + private static MatrixBlock[] computeSTFT(MatrixBlock re, MatrixBlock im, int windowSize, int overlap) { + if (re == null) { throw new DMLRuntimeException("Invalid empty block"); - - // run stft - if (in2 != null) { - in1.sparseToDense(); - in2.sparseToDense(); - return stft(in1, in2, windowSize, overlap); + } else if (im != null && !im.isEmptyBlock(false)) { + re.sparseToDense(); + im.sparseToDense(); + return stft(re, im, windowSize, overlap); } else { - in1.sparseToDense(); - return stft(in1, windowSize, overlap); + if (re.isEmptyBlock(false)) { + // Return the original matrix as the result + int stepSize = windowSize - overlap; + if (stepSize == 0) { + throw new IllegalArgumentException("(windowSize - overlap) is zero"); + } + int totalFrames = (re.getNumColumns() - overlap + stepSize - 1) / stepSize; + int out_len = totalFrames * windowSize; + double[] out_zero = new double[out_len]; + + return new MatrixBlock[]{new MatrixBlock(1, out_len, out_zero), new MatrixBlock(1, out_len, out_zero)}; + } + re.sparseToDense(); + return stft(re, windowSize, overlap); } } From be266e76e6a41c962b83b826ddff1262a95a94e1 Mon Sep 17 00:00:00 2001 From: mufwan Date: Thu, 15 Feb 2024 20:09:03 +0100 Subject: [PATCH 085/133] apply stft to each row --- dml_test/stft.dml | 6 +- fft.dml | 5 -- .../parser/BuiltinFunctionExpression.java | 8 +- .../runtime/matrix/data/LibCommonsMath.java | 14 +++- .../runtime/matrix/data/LibMatrixSTFT.java | 57 +++++--------- .../sysds/test/component/matrix/STFTTest.java | 78 +++++++++++++++++++ 6 files changed, 116 insertions(+), 52 deletions(-) delete mode 100644 fft.dml diff --git a/dml_test/stft.dml b/dml_test/stft.dml index 425ff9d8fcd..dcf5121c414 100644 --- a/dml_test/stft.dml +++ b/dml_test/stft.dml @@ -1,6 +1,6 @@ print("hello") -M = matrix("0 0 0 0 0 0", rows=1, cols=6) -I = matrix("0 0 0 0 0 0", rows=1, cols=6) -[r,i] = stft(M, I, 4, 3) +M = matrix("0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15", rows=4, cols=4) +I = matrix("0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0", rows=2, cols=8) +[r,i] = stft(M, 4, 5) print(toString(r)) print(toString(i)) diff --git a/fft.dml b/fft.dml deleted file mode 100644 index d5186897d09..00000000000 --- a/fft.dml +++ /dev/null @@ -1,5 +0,0 @@ -print("hello") -M = matrix("0 18 -15 3", rows=1, cols=4) -[r,i] = fft(M) -print(toString(r)) -print(toString(i)) \ No newline at end of file diff --git a/src/main/java/org/apache/sysds/parser/BuiltinFunctionExpression.java b/src/main/java/org/apache/sysds/parser/BuiltinFunctionExpression.java index 746c9c9577b..dc326143f77 100644 --- a/src/main/java/org/apache/sysds/parser/BuiltinFunctionExpression.java +++ b/src/main/java/org/apache/sysds/parser/BuiltinFunctionExpression.java @@ -524,6 +524,8 @@ else if(expressionOne != null){ if (!isPowerOfTwo(((ConstIdentifier) getSecondExpr().getOutput()).getLongValue())) { raiseValidateError("This FFT implementation is only defined for matrices with dimensions that are powers of 2." + "The window size (2nd argument) is not a power of two", false, LanguageErrorCodes.INVALID_PARAMETERS); + } else if (((ConstIdentifier) getSecondExpr().getOutput()).getLongValue() <= ((ConstIdentifier) getThirdExpr().getOutput()).getLongValue()) { + raiseValidateError("Overlap can't be larger than or equal to the window size.", false, LanguageErrorCodes.INVALID_PARAMETERS); } } else if (_args.length == 4) { checkMatrixParam(getSecondExpr()); @@ -532,10 +534,10 @@ else if(expressionOne != null){ if (!isPowerOfTwo(((ConstIdentifier) getThirdExpr().getOutput()).getLongValue())) { raiseValidateError("This FFT implementation is only defined for matrices with dimensions that are powers of 2." + "The window size (3rd argument) is not a power of two", false, LanguageErrorCodes.INVALID_PARAMETERS); - } else if (getFirstExpr().getOutput().getDim2() != getSecondExpr().getOutput().getDim2()) { + } else if (getFirstExpr().getOutput().getDim1() != getSecondExpr().getOutput().getDim1() || getFirstExpr().getOutput().getDim2() != getSecondExpr().getOutput().getDim2()) { raiseValidateError("The real and imaginary part of the provided matrix are of different dimensions.", false); - } else if (getFirstExpr().getOutput().getDim1() != 1 || getSecondExpr().getOutput().getDim1() != 1) { - raiseValidateError("This FFT implementation is only defined for one-dimensional matrices.", false, LanguageErrorCodes.INVALID_PARAMETERS); + } else if (((ConstIdentifier) getThirdExpr().getOutput()).getLongValue() <= ((ConstIdentifier) getFourthExpr().getOutput()).getLongValue()) { + raiseValidateError("Overlap can't be larger than or equal to the window size.", false, LanguageErrorCodes.INVALID_PARAMETERS); } } diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java index 44c6624f8d9..d90b7514fd2 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java @@ -464,15 +464,21 @@ private static MatrixBlock[] computeSTFT(MatrixBlock re, MatrixBlock im, int win } else { if (re.isEmptyBlock(false)) { // Return the original matrix as the result + int rows = re.getNumRows(); + int cols = re.getNumColumns(); + int stepSize = windowSize - overlap; if (stepSize == 0) { - throw new IllegalArgumentException("(windowSize - overlap) is zero"); + throw new IllegalArgumentException("windowSize - overlap is zero"); } - int totalFrames = (re.getNumColumns() - overlap + stepSize - 1) / stepSize; - int out_len = totalFrames * windowSize; + + int numberOfFramesPerRow = (cols - overlap + stepSize - 1) / stepSize; + int rowLength= numberOfFramesPerRow * windowSize; + int out_len = rowLength * rows; + double[] out_zero = new double[out_len]; - return new MatrixBlock[]{new MatrixBlock(1, out_len, out_zero), new MatrixBlock(1, out_len, out_zero)}; + return new MatrixBlock[]{new MatrixBlock(rows, rowLength, out_zero), new MatrixBlock(rows, rowLength, out_zero)}; } re.sparseToDense(); return stft(re, windowSize, overlap); diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixSTFT.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixSTFT.java index ff990129741..ae1fecd9e26 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixSTFT.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixSTFT.java @@ -18,6 +18,7 @@ */ package org.apache.sysds.runtime.matrix.data; +import org.apache.commons.math3.util.FastMath; import static org.apache.sysds.runtime.matrix.data.LibMatrixFourier.fft_one_dim; import static org.apache.sysds.runtime.matrix.data.LibMatrixFourier.isPowerOfTwo; @@ -37,58 +38,39 @@ public class LibMatrixSTFT { * @return array of two matrix blocks */ public static MatrixBlock[] stft(MatrixBlock re, MatrixBlock im, int windowSize, int overlap) { - return stft(re.getDenseBlockValues(), im.getDenseBlockValues(), windowSize, overlap); - } - - /** - * Function to perform STFT on two given double arrays with windowSize and overlap. - * The first one represents the real values and the second one the imaginary values. - * The output also contains one matrix for the real and one for the imaginary values. - * The results of the fourier transformations are appended to each other in the output. - * - * @param signal_re array representing the real part of the signal - * @param signal_im array representing the imaginary part of the signal - * @param windowSize size of window - * @param overlap size of overlap - * @return array of two matrix blocks - */ - public static MatrixBlock[] stft(double[] signal_re, double[] signal_im, int windowSize, int overlap) { - if (!isPowerOfTwo(windowSize)) { - throw new IllegalArgumentException("windowSize is not a power of two"); - } - - if (signal_re.length != signal_im.length) { - throw new IllegalArgumentException("different number of real and imaginary values"); - } + int rows = re.getNumRows(); + int cols = re.getNumColumns(); int stepSize = windowSize - overlap; if (stepSize == 0) { throw new IllegalArgumentException("windowSize - overlap is zero"); } - int totalFrames = (signal_re.length - overlap + stepSize - 1) / stepSize; - int out_len = totalFrames * windowSize; - // Initialize the STFT output array: [time frame][frequency bin][real & imaginary parts] + + //double frames = (double) (cols - overlap + stepSize - 1) / stepSize; // frames per row + //int numberOfFramesPerRow = (int) FastMath.ceil(frames); + int numberOfFramesPerRow = (cols - overlap + stepSize - 1) / stepSize; + int rowLength= numberOfFramesPerRow * windowSize; + int out_len = rowLength * rows; + double[] stftOutput_re = new double[out_len]; double[] stftOutput_im = new double[out_len]; double[] re_inter = new double[out_len]; double[] im_inter = new double[out_len]; - for (int i = 0; i < totalFrames; i++) { - for (int j = 0; j < windowSize; j++) { - stftOutput_re[i * windowSize + j] = ((i * stepSize + j) < signal_re.length) ? signal_re[i * stepSize + j] : 0; - stftOutput_im[i * windowSize + j] = ((i * stepSize + j) < signal_im.length) ? signal_im[i * stepSize + j] : 0; + for (int h = 0; h < rows; h++){ + for (int i = 0; i < numberOfFramesPerRow; i++) { + for (int j = 0; j < windowSize; j++) { + stftOutput_re[h * rowLength + i * windowSize + j] = ((i * stepSize + j) < cols) ? re.getDenseBlockValues()[h * cols + i * stepSize + j] : 0; + stftOutput_im[h * rowLength + i * windowSize + j] = ((i * stepSize + j) < cols) ? im.getDenseBlockValues()[h * cols + i * stepSize + j] : 0; + } + fft_one_dim(stftOutput_re, stftOutput_im, re_inter, im_inter, h * rowLength + i * windowSize, h * rowLength + (i+1) * windowSize, windowSize, 1); } } - for (int frame = 0; frame < totalFrames; frame++) { - // Perform FFT on windowed signal - fft_one_dim(stftOutput_re, stftOutput_im, re_inter, im_inter, frame * windowSize, (frame+1) * windowSize, windowSize, 1); - } - - return new MatrixBlock[]{new MatrixBlock(1, out_len, stftOutput_re), new MatrixBlock(1, out_len, stftOutput_im)}; + return new MatrixBlock[]{new MatrixBlock(rows, rowLength, stftOutput_re), new MatrixBlock(rows, rowLength, stftOutput_im)}; } /** @@ -103,7 +85,8 @@ public static MatrixBlock[] stft(double[] signal_re, double[] signal_im, int win * @return array of two matrix blocks */ public static MatrixBlock[] stft(MatrixBlock re, int windowSize, int overlap){ - return stft(re.getDenseBlockValues(), new double[re.getDenseBlockValues().length], windowSize, overlap); + //return stft(re.getDenseBlockValues(), new double[re.getDenseBlockValues().length], windowSize, overlap); + return stft(re, new MatrixBlock(re.getNumRows(), re.getNumColumns(), new double[re.getNumRows() * re.getNumColumns()]), windowSize, overlap); } } diff --git a/src/test/java/org/apache/sysds/test/component/matrix/STFTTest.java b/src/test/java/org/apache/sysds/test/component/matrix/STFTTest.java index 5e18a87e8e2..34480c12891 100644 --- a/src/test/java/org/apache/sysds/test/component/matrix/STFTTest.java +++ b/src/test/java/org/apache/sysds/test/component/matrix/STFTTest.java @@ -45,6 +45,30 @@ public void simple_test() { } + @Test + public void simple_test_two() { + + MatrixBlock re = new MatrixBlock(1, 15, new double[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}); + + MatrixBlock[] res = stft(re, 4, 2); + + double[] res_re = res[0].getDenseBlockValues(); + double[] res_im = res[1].getDenseBlockValues(); + + double[] expected_re = {6, -2, -2, -2, 14, -2, -2, -2, 22, -2, -2, -2, 30, -2, -2, -2, 38, -2, -2, -2, 46, -2, -2, -2, 39, -2, 13, -2}; + double[] expected_im = {0, 2, 0, -2, 0, 2, 0, -2, 0, 2, 0, -2, 0, 2, 0, -2, 0, 2, 0, -2, 0, 2, 0, -2, 0, -13, 0, 13}; + + /* + for (int i = 0; i < res_re.length; i++) { + System.out.println(res_re[i] + " " + res_im[i]); + } + + */ + assertArrayEquals(expected_re, res_re, 0.0001); + assertArrayEquals(expected_im, res_im, 0.0001); + + } + @Test public void matrix_block_one_dim_test(){ @@ -100,4 +124,58 @@ public void matrix_block_one_dim_test3(){ } + @Test + public void test_two_x_eight() { + + MatrixBlock re = new MatrixBlock(2, 8, new double[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}); + + MatrixBlock[] res = stft(re, 4, 1); + + double[] res_re = res[0].getDenseBlockValues(); + double[] res_im = res[1].getDenseBlockValues(); + + double[] expected_re = {6, -2, -2, -2, 18, -2, -2, -2, 13, 6, -1, 6, 38, -2, -2, -2, 50, -2, -2, -2, 29, 14, -1, 14}; + double[] expected_im = {0, 2, 0, -2, 0, 2, 0, -2, 0, -7, 0, 7, 0, 2, 0, -2, 0, 2, 0, -2, 0, -15, 0, 15}; + + assertArrayEquals(expected_re, res_re, 0.0001); + assertArrayEquals(expected_im, res_im, 0.0001); + + } + + @Test + public void test_four_x_four() { + + MatrixBlock re = new MatrixBlock(4, 4, new double[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}); + + MatrixBlock[] res = stft(re, 4, 0); + + double[] res_re = res[0].getDenseBlockValues(); + double[] res_im = res[1].getDenseBlockValues(); + + double[] expected_re = {6, -2, -2, -2, 22, -2, -2, -2, 38, -2, -2, -2, 54, -2, -2, -2}; + double[] expected_im = {0, 2, 0, -2, 0, 2, 0, -2, 0, 2, 0, -2, 0, 2, 0, -2}; + + assertArrayEquals(expected_re, res_re, 0.0001); + assertArrayEquals(expected_im, res_im, 0.0001); + + } + + @Test + public void test_four_x_four_two() { + + MatrixBlock re = new MatrixBlock(4, 5, new double[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19}); + + MatrixBlock[] res = stft(re, 4, 1); + + double[] res_re = res[0].getDenseBlockValues(); + double[] res_im = res[1].getDenseBlockValues(); + + double[] expected_re = {6.0, -2.0, -2.0, -2.0, 7.0, 3.0, -1.0, 3.0, 26.0, -2.0, -2.0, -2.0, 17.0, 8.0, -1.0, 8.0, 46.0, -2.0, -2.0, -2.0, 27.0, 13.0, -1.0, 13.0, 66.0, -2.0, -2.0, -2.0, 37.0, 18.0, -1.0, 18.0}; + double[] expected_im = {0.0, 2.0, 0.0, -2.0, 0.0, -4.0, 0.0, 4.0, 0.0, 2.0, 0.0, -2.0, 0.0, -9.0, 0.0, 9.0, 0.0, 2.0, 0.0, -2.0, 0.0, -14.0, 0.0, 14.0, 0.0, 2.0, 0.0, -2.0, 0.0, -19.0, 0.0, 19.0}; + + assertArrayEquals(expected_re, res_re, 0.0001); + assertArrayEquals(expected_im, res_im, 0.0001); + + } + } From 682edbc15e3786ffd64356846408084b38d9a332 Mon Sep 17 00:00:00 2001 From: mufwan Date: Fri, 16 Feb 2024 21:25:13 +0100 Subject: [PATCH 086/133] classifier for keyword spotting --- dml_test/keywordSpotting.dml | 41 ++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 dml_test/keywordSpotting.dml diff --git a/dml_test/keywordSpotting.dml b/dml_test/keywordSpotting.dml new file mode 100644 index 00000000000..f9a27ab16e6 --- /dev/null +++ b/dml_test/keywordSpotting.dml @@ -0,0 +1,41 @@ + + +X = read("shuffled_features.csv", format="csv") +y = read("shuffled_labels_minus_and_plus.csv", format="csv") + +# Add a train test split. +[X_train, X_test, y_train, y_test] = split(X=X, Y=y, seed= 13) + +# Preprocess with fft Optionally move before split. +[X_train_re, X_train_im] = stft(X_train, 256, 128) +[X_test_re, X_test_im] = stft(X_test, 256, 128) + + +X_train_re_sq = X_train_re^2 +X_train_im_sq = X_train_im^2 + + +X_test_re_sq = X_test_re^2 +X_test_im_sq = X_test_im^2 + + +# Sum the squared matrices +sum_X_train_sq = X_train_re_sq + X_train_im_sq +sum_X_test_sq = X_test_re_sq + X_test_im_sq + +# Compute the square root of each element in the sum matrix to get the magnitudes +magnitudes_train = sqrt(sum_X_train_sq) +magnitudes_test = sqrt(sum_X_test_sq) + +bias = lm(X=magnitudes_train, y=y_train, reg=1e-3, maxi=5, verbose=TRUE) +predictions = lmPredict(X=magnitudes_test, B=bias, verbose=FALSE) + +sign_predictions = sign(predictions) +sign_Y = sign(y_test) + +correct = sum(sign_predictions == sign_Y) +total = nrow(y_test) +accuracy = correct / total + +#print(toString(predictions)) +print(toString(accuracy)) From 297b815ae2f861eb7b6beb09400d4a9a78a41d92 Mon Sep 17 00:00:00 2001 From: mufwan Date: Sat, 17 Feb 2024 20:45:23 +0100 Subject: [PATCH 087/133] update stft --- .../parser/BuiltinFunctionExpression.java | 24 +++++++++++++++++-- .../runtime/matrix/data/LibCommonsMath.java | 14 +++++++++-- .../runtime/matrix/data/LibMatrixSTFT.java | 6 +++-- 3 files changed, 38 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/apache/sysds/parser/BuiltinFunctionExpression.java b/src/main/java/org/apache/sysds/parser/BuiltinFunctionExpression.java index dc326143f77..edbd5cead45 100644 --- a/src/main/java/org/apache/sysds/parser/BuiltinFunctionExpression.java +++ b/src/main/java/org/apache/sysds/parser/BuiltinFunctionExpression.java @@ -440,23 +440,33 @@ else if (!isPowerOfTwo(expressionOne.getOutput().getDim2())) { } checkNumParameters(1); - checkMatrixParam(expressionOne); + checkMatrixParam(getFirstExpr()); + // setup output properties DataIdentifier fftOut1 = (DataIdentifier) getOutputs()[0]; DataIdentifier fftOut2 = (DataIdentifier) getOutputs()[1]; + // TODO: Add Validation + // if (getFirstExpr().getOutput().getDim2() != 1 || + // getFirstExpr().getOutput().getDim2() != 2) { + // raiseValidateError("Eigen Decomposition can only be done on a square matrix. + // Input matrix is rectangular (rows=" + getFirstExpr().getOutput().getDim1() + + // ", cols="+ getFirstExpr().getOutput().getDim2() +")", conditional); + // } + + // Output1 - FFT Values fftOut1.setDataType(DataType.MATRIX); fftOut1.setValueType(ValueType.FP64); fftOut1.setDimensions(getFirstExpr().getOutput().getDim1(), getFirstExpr().getOutput().getDim2()); fftOut1.setBlocksize(getFirstExpr().getOutput().getBlocksize()); + // Output2 - FFT Vectors fftOut2.setDataType(DataType.MATRIX); fftOut2.setValueType(ValueType.FP64); fftOut2.setDimensions(getFirstExpr().getOutput().getDim1(), getFirstExpr().getOutput().getDim2()); fftOut2.setBlocksize(getFirstExpr().getOutput().getBlocksize()); break; - } case IFFT_LINEARIZED: { Expression expressionTwo = getSecondExpr(); @@ -496,11 +506,21 @@ else if(expressionOne != null){ DataIdentifier ifftOut1 = (DataIdentifier) getOutputs()[0]; DataIdentifier ifftOut2 = (DataIdentifier) getOutputs()[1]; + // TODO: Add Validation + // if (getFirstExpr().getOutput().getDim2() != 1 || + // getFirstExpr().getOutput().getDim2() != 2) { + // raiseValidateError("Eigen Decomposition can only be done on a square matrix. + // Input matrix is rectangular (rows=" + getFirstExpr().getOutput().getDim1() + + // ", cols="+ getFirstExpr().getOutput().getDim2() +")", conditional); + // } + + // Output1 - ifft Values ifftOut1.setDataType(DataType.MATRIX); ifftOut1.setValueType(ValueType.FP64); ifftOut1.setDimensions(getFirstExpr().getOutput().getDim1(), getFirstExpr().getOutput().getDim2()); ifftOut1.setBlocksize(getFirstExpr().getOutput().getBlocksize()); + // Output2 - ifft Vectors ifftOut2.setDataType(DataType.MATRIX); ifftOut2.setValueType(ValueType.FP64); ifftOut2.setDimensions(getFirstExpr().getOutput().getDim1(), getFirstExpr().getOutput().getDim2()); diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java index d90b7514fd2..0dcf3ac9c8b 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java @@ -123,7 +123,7 @@ public static MatrixBlock[] multiReturnOperations(MatrixBlock in, String opcode, public static MatrixBlock[] multiReturnOperations(MatrixBlock in, String opcode, int windowSize, int overlap) { if(opcode.equals("stft")) - return computeSTFT(in, null, windowSize, overlap); + return computeSTFT(in, windowSize, overlap); else if(opcode.equals("qr")) return computeQR(in); else if (opcode.equals("qr2")) @@ -141,7 +141,7 @@ else if (opcode.equals("svd")) else if (opcode.equals("fft")) return computeFFT(in); else if (opcode.equals("ifft")) - return computeIFFT(in, null); + return computeIFFT(in); return null; } @@ -485,6 +485,16 @@ private static MatrixBlock[] computeSTFT(MatrixBlock re, MatrixBlock im, int win } } + /** + * Function to perform STFT on a given matrix. + * + * @param re matrix object + * @return array of matrix blocks + */ + private static MatrixBlock[] computeSTFT(MatrixBlock re, int windowSize, int overlap) { + return computeSTFT(re, null, windowSize, overlap); + } + /** * Performs Singular Value Decomposition. Calls Apache Commons Math SVD. * X = U * Sigma * Vt, where X is the input matrix, diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixSTFT.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixSTFT.java index ae1fecd9e26..3c9d449deaf 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixSTFT.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixSTFT.java @@ -63,8 +63,10 @@ public static MatrixBlock[] stft(MatrixBlock re, MatrixBlock im, int windowSize, for (int h = 0; h < rows; h++){ for (int i = 0; i < numberOfFramesPerRow; i++) { for (int j = 0; j < windowSize; j++) { - stftOutput_re[h * rowLength + i * windowSize + j] = ((i * stepSize + j) < cols) ? re.getDenseBlockValues()[h * cols + i * stepSize + j] : 0; - stftOutput_im[h * rowLength + i * windowSize + j] = ((i * stepSize + j) < cols) ? im.getDenseBlockValues()[h * cols + i * stepSize + j] : 0; + if ((i * stepSize + j) < cols) { + stftOutput_re[h * rowLength + i * windowSize + j] = re.getDenseBlockValues()[h * cols + i * stepSize + j]; + stftOutput_im[h * rowLength + i * windowSize + j] = im.getDenseBlockValues()[h * cols + i * stepSize + j]; + } } fft_one_dim(stftOutput_re, stftOutput_im, re_inter, im_inter, h * rowLength + i * windowSize, h * rowLength + (i+1) * windowSize, windowSize, 1); } From bb4f520ded1c83fd54dceb09f678093ae4d10e1a Mon Sep 17 00:00:00 2001 From: mufwan Date: Mon, 19 Feb 2024 22:04:25 +0100 Subject: [PATCH 088/133] add parallelization --- .../parser/BuiltinFunctionExpression.java | 2 +- .../runtime/matrix/data/LibCommonsMath.java | 6 +-- .../runtime/matrix/data/LibMatrixSTFT.java | 44 ++++++++++++++++--- 3 files changed, 41 insertions(+), 11 deletions(-) diff --git a/src/main/java/org/apache/sysds/parser/BuiltinFunctionExpression.java b/src/main/java/org/apache/sysds/parser/BuiltinFunctionExpression.java index edbd5cead45..aef51a2fc12 100644 --- a/src/main/java/org/apache/sysds/parser/BuiltinFunctionExpression.java +++ b/src/main/java/org/apache/sysds/parser/BuiltinFunctionExpression.java @@ -357,7 +357,7 @@ public void validateExpression(MultiAssignmentStatement stmt, HashMap> tasks = new ArrayList<>(); + + try { + for (int h = 0; h < rows; h++){ + for (int i = 0; i < numberOfFramesPerRow; i++) { + for (int j = 0; j < windowSize; j++) { + if ((i * stepSize + j) < cols) { + stftOutput_re[h * rowLength + i * windowSize + j] = re.getDenseBlockValues()[h * cols + i * stepSize + j]; + stftOutput_im[h * rowLength + i * windowSize + j] = im.getDenseBlockValues()[h * cols + i * stepSize + j]; + } } + final int finalI = i; + final int finalH = h; + tasks.add( pool.submit(() -> { + fft_one_dim(stftOutput_re, stftOutput_im, re_inter, im_inter, finalH * rowLength + finalI * windowSize, finalH * rowLength + (finalI+1) * windowSize, windowSize, 1); + })); } - fft_one_dim(stftOutput_re, stftOutput_im, re_inter, im_inter, h * rowLength + i * windowSize, h * rowLength + (i+1) * windowSize, windowSize, 1); } + for(Future f : tasks) + f.get(); + } + catch(InterruptedException | ExecutionException e) { + throw new RuntimeException(e); + } + finally { + pool.shutdown(); } return new MatrixBlock[]{new MatrixBlock(rows, rowLength, stftOutput_re), new MatrixBlock(rows, rowLength, stftOutput_im)}; From 48eec91f5abf662600aa804b3ab02d8f70b5e31c Mon Sep 17 00:00:00 2001 From: mufwan Date: Tue, 20 Feb 2024 00:07:29 +0100 Subject: [PATCH 089/133] Fixed error --- dml_test/stft.dml | 2 +- .../apache/sysds/runtime/instructions/CPInstructionParser.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dml_test/stft.dml b/dml_test/stft.dml index dcf5121c414..f9d224ce29c 100644 --- a/dml_test/stft.dml +++ b/dml_test/stft.dml @@ -1,6 +1,6 @@ print("hello") M = matrix("0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15", rows=4, cols=4) I = matrix("0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0", rows=2, cols=8) -[r,i] = stft(M, 4, 5) +[r,i] = stft(M, 4, 1) print(toString(r)) print(toString(i)) diff --git a/src/main/java/org/apache/sysds/runtime/instructions/CPInstructionParser.java b/src/main/java/org/apache/sysds/runtime/instructions/CPInstructionParser.java index 91c302b4ea1..1a7b2b78b70 100644 --- a/src/main/java/org/apache/sysds/runtime/instructions/CPInstructionParser.java +++ b/src/main/java/org/apache/sysds/runtime/instructions/CPInstructionParser.java @@ -337,7 +337,7 @@ public class CPInstructionParser extends InstructionParser { String2CPInstructionType.put("ifft", CPType.MultiReturnComplexMatrixBuiltin); String2CPInstructionType.put("fft_linearized", CPType.MultiReturnBuiltin); String2CPInstructionType.put("ifft_linearized", CPType.MultiReturnComplexMatrixBuiltin); - String2CPInstructionType.put( "stft", CPType.MultiReturnBuiltin); + String2CPInstructionType.put( "stft", CPType.MultiReturnComplexMatrixBuiltin); String2CPInstructionType.put( "svd", CPType.MultiReturnBuiltin); String2CPInstructionType.put( "partition", CPType.Partition); From 12297d038ff2a07cf5c23e31bc1c608ab71c78c0 Mon Sep 17 00:00:00 2001 From: mufwan Date: Tue, 20 Feb 2024 00:35:15 +0100 Subject: [PATCH 090/133] add threads --- .../runtime/matrix/data/LibCommonsMath.java | 18 ++++----- .../runtime/matrix/data/LibMatrixSTFT.java | 40 +++++++------------ .../sysds/test/component/matrix/STFTTest.java | 18 +++++---- 3 files changed, 33 insertions(+), 43 deletions(-) diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java index 04e8553bc5f..6a25b6aec97 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java @@ -107,11 +107,11 @@ else if (opcode.equals("cholesky")) } public static MatrixBlock[] multiReturnOperations(MatrixBlock in, String opcode) { - return multiReturnOperations(in, opcode, 1, 1); + return multiReturnOperations(in, opcode, 1, (long) 1); } public static MatrixBlock[] multiReturnOperations(MatrixBlock in1, MatrixBlock in2, String opcode) { - return multiReturnOperations(in1, in2, opcode, 1, 1); + return multiReturnOperations(in1, in2, opcode, 1, (long) 1); } public static MatrixBlock[] multiReturnOperations(MatrixBlock in, String opcode, int threads, int num_iterations, double tol) { @@ -123,7 +123,7 @@ public static MatrixBlock[] multiReturnOperations(MatrixBlock in, String opcode, public static MatrixBlock[] multiReturnOperations(MatrixBlock in, String opcode, int windowSize, int overlap) { if(opcode.equals("stft")) - return computeSTFT(in, windowSize, overlap); + return computeSTFT(in, windowSize, overlap, 1); else if(opcode.equals("qr")) return computeQR(in); else if (opcode.equals("qr2")) @@ -147,7 +147,7 @@ else if (opcode.equals("ifft")) public static MatrixBlock[] multiReturnOperations(MatrixBlock in1, MatrixBlock in2, String opcode, int windowSize, int overlap) { if(opcode.equals("stft")) - return computeSTFT(in1, in2, windowSize, overlap); + return computeSTFT(in1, in2, windowSize, overlap, 1); else if(opcode.equals("ifft")) return computeIFFT(in1, in2, 1); else @@ -454,13 +454,13 @@ private static MatrixBlock[] computeIFFT_LINEARIZED(MatrixBlock re, int threads) * @param overlap of stft * @return array of matrix blocks */ - private static MatrixBlock[] computeSTFT(MatrixBlock re, MatrixBlock im, int windowSize, int overlap) { + private static MatrixBlock[] computeSTFT(MatrixBlock re, MatrixBlock im, int windowSize, int overlap, int threads) { if (re == null) { throw new DMLRuntimeException("Invalid empty block"); } else if (im != null && !im.isEmptyBlock(false)) { re.sparseToDense(); im.sparseToDense(); - return stft(re, im, windowSize, overlap); + return stft(re, im, windowSize, overlap, threads); } else { if (re.isEmptyBlock(false)) { // Return the original matrix as the result @@ -481,7 +481,7 @@ private static MatrixBlock[] computeSTFT(MatrixBlock re, MatrixBlock im, int win return new MatrixBlock[]{new MatrixBlock(rows, rowLength, out_zero), new MatrixBlock(rows, rowLength, out_zero)}; } re.sparseToDense(); - return stft(re, windowSize, overlap); + return stft(re, windowSize, overlap, threads); } } @@ -491,8 +491,8 @@ private static MatrixBlock[] computeSTFT(MatrixBlock re, MatrixBlock im, int win * @param re matrix object * @return array of matrix blocks */ - private static MatrixBlock[] computeSTFT(MatrixBlock re, int windowSize, int overlap) { - return computeSTFT(re, null, windowSize, overlap); + private static MatrixBlock[] computeSTFT(MatrixBlock re, int windowSize, int overlap, int threads) { + return computeSTFT(re, null, windowSize, overlap, threads); } /** diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixSTFT.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixSTFT.java index 0aab0f0d170..878dda9ab5e 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixSTFT.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixSTFT.java @@ -18,21 +18,15 @@ */ package org.apache.sysds.runtime.matrix.data; -import org.apache.commons.math3.util.FastMath; import static org.apache.sysds.runtime.matrix.data.LibMatrixFourier.fft_one_dim; -import static org.apache.sysds.runtime.matrix.data.LibMatrixFourier.isPowerOfTwo; - import org.apache.sysds.runtime.util.CommonThreadPool; - import java.util.ArrayList; import java.util.concurrent.Future; import java.util.concurrent.ExecutorService; import java.util.concurrent.ExecutionException; -import org.apache.sysds.runtime.util.CommonThreadPool; import java.util.List; -import java.util.concurrent.TimeUnit; public class LibMatrixSTFT { @@ -48,7 +42,7 @@ public class LibMatrixSTFT { * @param overlap size of overlap * @return array of two matrix blocks */ - public static MatrixBlock[] stft(MatrixBlock re, MatrixBlock im, int windowSize, int overlap) { + public static MatrixBlock[] stft(MatrixBlock re, MatrixBlock im, int windowSize, int overlap, int threads) { int rows = re.getNumRows(); int cols = re.getNumColumns(); @@ -58,9 +52,6 @@ public static MatrixBlock[] stft(MatrixBlock re, MatrixBlock im, int windowSize, throw new IllegalArgumentException("windowSize - overlap is zero"); } - - //double frames = (double) (cols - overlap + stepSize - 1) / stepSize; // frames per row - //int numberOfFramesPerRow = (int) FastMath.ceil(frames); int numberOfFramesPerRow = (cols - overlap + stepSize - 1) / stepSize; int rowLength= numberOfFramesPerRow * windowSize; int out_len = rowLength * rows; @@ -71,26 +62,24 @@ public static MatrixBlock[] stft(MatrixBlock re, MatrixBlock im, int windowSize, double[] re_inter = new double[out_len]; double[] im_inter = new double[out_len]; - int threads = 1; final ExecutorService pool = CommonThreadPool.get(threads); final List> tasks = new ArrayList<>(); try { for (int h = 0; h < rows; h++){ - for (int i = 0; i < numberOfFramesPerRow; i++) { - for (int j = 0; j < windowSize; j++) { - if ((i * stepSize + j) < cols) { - stftOutput_re[h * rowLength + i * windowSize + j] = re.getDenseBlockValues()[h * cols + i * stepSize + j]; - stftOutput_im[h * rowLength + i * windowSize + j] = im.getDenseBlockValues()[h * cols + i * stepSize + j]; + final int finalH = h; + tasks.add( pool.submit(() -> { + for (int i = 0; i < numberOfFramesPerRow; i++) { + for (int j = 0; j < windowSize; j++) { + if ((i * stepSize + j) < cols) { + stftOutput_re[finalH * rowLength + i * windowSize + j] = re.getDenseBlockValues()[finalH * cols + i * stepSize + j]; + stftOutput_im[finalH * rowLength + i * windowSize + j] = im.getDenseBlockValues()[finalH * cols + i * stepSize + j]; + } } + fft_one_dim(stftOutput_re, stftOutput_im, re_inter, im_inter, finalH * rowLength + i * windowSize, finalH * rowLength + (i + 1) * windowSize, windowSize, 1); } - final int finalI = i; - final int finalH = h; - tasks.add( pool.submit(() -> { - fft_one_dim(stftOutput_re, stftOutput_im, re_inter, im_inter, finalH * rowLength + finalI * windowSize, finalH * rowLength + (finalI+1) * windowSize, windowSize, 1); - })); - } + })); } for(Future f : tasks) f.get(); @@ -116,9 +105,8 @@ public static MatrixBlock[] stft(MatrixBlock re, MatrixBlock im, int windowSize, * @param overlap size of overlap * @return array of two matrix blocks */ - public static MatrixBlock[] stft(MatrixBlock re, int windowSize, int overlap){ - //return stft(re.getDenseBlockValues(), new double[re.getDenseBlockValues().length], windowSize, overlap); - return stft(re, new MatrixBlock(re.getNumRows(), re.getNumColumns(), new double[re.getNumRows() * re.getNumColumns()]), windowSize, overlap); + public static MatrixBlock[] stft(MatrixBlock re, int windowSize, int overlap, int threads){ + return stft(re, new MatrixBlock(re.getNumRows(), re.getNumColumns(), new double[re.getNumRows() * re.getNumColumns()]), windowSize, overlap, threads); } -} +} \ No newline at end of file diff --git a/src/test/java/org/apache/sysds/test/component/matrix/STFTTest.java b/src/test/java/org/apache/sysds/test/component/matrix/STFTTest.java index 34480c12891..3dcca95da65 100644 --- a/src/test/java/org/apache/sysds/test/component/matrix/STFTTest.java +++ b/src/test/java/org/apache/sysds/test/component/matrix/STFTTest.java @@ -27,12 +27,14 @@ public class STFTTest { + int threads = Runtime.getRuntime().availableProcessors(); + @Test public void simple_test() { MatrixBlock re = new MatrixBlock(1, 16, new double[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}); - MatrixBlock[] res = stft(re, 4, 2); + MatrixBlock[] res = stft(re, 4, 2, threads); double[] res_re = res[0].getDenseBlockValues(); double[] res_im = res[1].getDenseBlockValues(); @@ -50,7 +52,7 @@ public void simple_test_two() { MatrixBlock re = new MatrixBlock(1, 15, new double[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}); - MatrixBlock[] res = stft(re, 4, 2); + MatrixBlock[] res = stft(re, 4, 2, threads); double[] res_re = res[0].getDenseBlockValues(); double[] res_im = res[1].getDenseBlockValues(); @@ -74,7 +76,7 @@ public void matrix_block_one_dim_test(){ MatrixBlock re = new MatrixBlock(1, 4, new double[]{0, 18, -15, 3}); - MatrixBlock[] res = stft(re, 4, 0); + MatrixBlock[] res = stft(re, 4, 0, threads); double[] res_re = res[0].getDenseBlockValues(); double[] res_im = res[1].getDenseBlockValues(); @@ -92,7 +94,7 @@ public void matrix_block_one_dim_test2(){ MatrixBlock re = new MatrixBlock(1, 8, new double[]{10, 5, -3, 8, 15, -6, 2, 0}); - MatrixBlock[] res = stft(re, 4, 2); + MatrixBlock[] res = stft(re, 4, 2, threads); double[] res_re = res[0].getDenseBlockValues(); double[] res_im = res[1].getDenseBlockValues(); @@ -111,7 +113,7 @@ public void matrix_block_one_dim_test3(){ MatrixBlock re = new MatrixBlock(1, 8, new double[]{10, 5, -3, 8, 15, -6, 2, 0}); MatrixBlock im = new MatrixBlock(1, 8, new double[]{0, 0, 0, 0, 0, 0, 0, 0}); - MatrixBlock[] res = stft(re, im, 4, 2); + MatrixBlock[] res = stft(re, im, 4, 2, threads); double[] res_re = res[0].getDenseBlockValues(); double[] res_im = res[1].getDenseBlockValues(); @@ -129,7 +131,7 @@ public void test_two_x_eight() { MatrixBlock re = new MatrixBlock(2, 8, new double[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}); - MatrixBlock[] res = stft(re, 4, 1); + MatrixBlock[] res = stft(re, 4, 1, threads); double[] res_re = res[0].getDenseBlockValues(); double[] res_im = res[1].getDenseBlockValues(); @@ -147,7 +149,7 @@ public void test_four_x_four() { MatrixBlock re = new MatrixBlock(4, 4, new double[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}); - MatrixBlock[] res = stft(re, 4, 0); + MatrixBlock[] res = stft(re, 4, 0, threads); double[] res_re = res[0].getDenseBlockValues(); double[] res_im = res[1].getDenseBlockValues(); @@ -165,7 +167,7 @@ public void test_four_x_four_two() { MatrixBlock re = new MatrixBlock(4, 5, new double[]{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19}); - MatrixBlock[] res = stft(re, 4, 1); + MatrixBlock[] res = stft(re, 4, 1, threads); double[] res_re = res[0].getDenseBlockValues(); double[] res_im = res[1].getDenseBlockValues(); From e1cc9a27d57f523736200d0284675ba42060fedb Mon Sep 17 00:00:00 2001 From: mufwan Date: Fri, 19 Jan 2024 20:15:31 +0100 Subject: [PATCH 091/133] stft without double[][] --- backup.txt | 108 ++++++++++++++++ .../component/matrix/LibMatrixSTFTTest.java | 117 ++++++++++++++++++ 2 files changed, 225 insertions(+) create mode 100644 backup.txt create mode 100644 src/test/java/org/apache/sysds/test/component/matrix/LibMatrixSTFTTest.java diff --git a/backup.txt b/backup.txt new file mode 100644 index 00000000000..4031c1ff6a2 --- /dev/null +++ b/backup.txt @@ -0,0 +1,108 @@ +package org.apache.sysds.runtime.matrix.data; + +import java.util.Arrays; + +public class LibMatrixFourier2 { + + /** + * Function to perform Fast Fourier Transformation on a given array. + * + * @param in array of ComplexDoubles + * @return array of ComplexDoubles + */ + public static ComplexDouble[] fft(double[] in) { + boolean wasPadded = false; + int originalLength = in.length; + + ComplexDouble[] complex = new ComplexDouble[in.length]; + for(int i=0; i Date: Sat, 20 Jan 2024 14:22:26 +0100 Subject: [PATCH 092/133] styling --- .../org/apache/sysds/runtime/matrix/data/LibMatrixSTFT.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixSTFT.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixSTFT.java index 878dda9ab5e..edea46bea33 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixSTFT.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixSTFT.java @@ -109,4 +109,4 @@ public static MatrixBlock[] stft(MatrixBlock re, int windowSize, int overlap, in return stft(re, new MatrixBlock(re.getNumRows(), re.getNumColumns(), new double[re.getNumRows() * re.getNumColumns()]), windowSize, overlap, threads); } -} \ No newline at end of file +} From baf7df7de464103dec528427f4f8e726f14f190e Mon Sep 17 00:00:00 2001 From: Jessica Priebe Date: Sat, 20 Jan 2024 15:44:11 +0100 Subject: [PATCH 093/133] styling --- backup.txt | 108 ----- .../sysds/runtime/io/ReaderWavFile.java | 102 ++++ .../matrix/data/LibMatrixKeywordSpotting.java | 193 ++++++++ .../test/component/matrix/FourierTestData.py | 214 +++++++++ .../matrix/FourierTestWithFiles.java | 453 ++++++++++++++++++ .../component/matrix/LibMatrixSTFTTest.java | 121 +++-- 6 files changed, 1016 insertions(+), 175 deletions(-) delete mode 100644 backup.txt create mode 100644 src/main/java/org/apache/sysds/runtime/io/ReaderWavFile.java create mode 100644 src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java create mode 100644 src/test/java/org/apache/sysds/test/component/matrix/FourierTestData.py create mode 100644 src/test/java/org/apache/sysds/test/component/matrix/FourierTestWithFiles.java diff --git a/backup.txt b/backup.txt deleted file mode 100644 index 4031c1ff6a2..00000000000 --- a/backup.txt +++ /dev/null @@ -1,108 +0,0 @@ -package org.apache.sysds.runtime.matrix.data; - -import java.util.Arrays; - -public class LibMatrixFourier2 { - - /** - * Function to perform Fast Fourier Transformation on a given array. - * - * @param in array of ComplexDoubles - * @return array of ComplexDoubles - */ - public static ComplexDouble[] fft(double[] in) { - boolean wasPadded = false; - int originalLength = in.length; - - ComplexDouble[] complex = new ComplexDouble[in.length]; - for(int i=0; i samples = new ArrayList<>(); + List labels = new ArrayList<>(); + + public LibMatrixKeywordSpotting() { + + // load all data + // data: http://storage.googleapis.com/download.tensorflow.org/data/mini_speech_commands.zip + // zip contains command folders which contain corresponding .wav files + // maybe change label to int? + loadAllData(); + + // convert waveforms to magnitudes of spectrogram + // uses stft + for (int i = 0; i < samples.size(); i++){ + double[] wave = samples.get(i); + double[] magnitudes = convertWaveToMagnitudesSpectrogram(wave); + samples.set(i, magnitudes); + } + + // TODO: + // train model + // use gaussianClassifier??? + // [prior, means, covs, det] = gaussianClassifier(D=X, C=y, varSmoothing=$2); + // use global variables for classifier + } + + private double[] convertWaveToMagnitudesSpectrogram(double[] wave){ + + // length=255, overlap=128 + // TODO: adjust stft + double[][] spectrogram = LibMatrixSTFT.one_dim_stft(wave, 255, 128); + + int cols = spectrogram[0].length; + double[] magnitudes = new double[cols]; + for (int i = 0; i < cols; i++){ + magnitudes[i] = Math.sqrt(Math.pow(spectrogram[0][i], 2) + Math.pow(spectrogram[0][i], 2)); + } + + return magnitudes; + } + + public String predictCommandForFile(String filePath){ + + // read wave file + double[] wave = ReaderWavFile.readMonoAudioFromWavFile(filePath); + + // convert waveforms to spectrogram + double[] magnitudes = convertWaveToMagnitudesSpectrogram(wave); + + // use global variables for prediction + // TODO + + return null; + } + + private void loadAllData(){ + + // doesn't work for url + // String url = "http://storage.googleapis.com/download.tensorflow.org/data/mini_speech_commands.zip"; + // Set dirs = Set.of("yes", "no"); + + String zipFilePath = "/Users/jessica/desktop/mini_speech_commands.zip"; + + try { + // get zip data + byte[] zipData = getZipData(new FileInputStream(zipFilePath)); + + // get folder names + Set dirs = getDirectories(zipData); + + for (String dir : dirs) { + readWavFilesDirectory(zipData, dir); + } + + } catch (IOException e) { + e.printStackTrace(); + } + + } + + private Set getDirectories(byte[] zipData) throws IOException { + + Set dirs = new HashSet<>(); + ZipInputStream stream = new ZipInputStream(new ByteArrayInputStream(zipData)); + + // exclude main directory + ZipEntry entry = stream.getNextEntry(); + int mainDirLength = entry.getName().length(); + + while ((entry = stream.getNextEntry()) != null) { + if (entry.isDirectory()) { + String dir = entry.getName(); + // remove / at the end + dirs.add(dir.substring(mainDirLength, dir.length() - 1)); + } + } + + return dirs; + } + + private void readWavFilesDirectory(byte[] zipData, String dir) throws IOException { + + ZipInputStream stream = new ZipInputStream(new ByteArrayInputStream(zipData)); + ZipEntry entry; + + while ((entry = stream.getNextEntry()) != null) { + if (entry.getName().startsWith(dir) && entry.isDirectory()) { + readWavFilesDirectory(stream, dir); + // dont read next dir + break; + } + } + + } + + private void readWavFilesDirectory(ZipInputStream stream, String dir) throws IOException { + + ZipEntry entry; + while ((entry = stream.getNextEntry()) != null && !entry.isDirectory() && entry.getName().endsWith(".wav")) { + readWavFile(entry, dir); + } + + } + + private void readWavFile(ZipEntry entry, String dir) { + + InputStream stream = new ByteArrayInputStream(entry.getExtra()); + double[] data = ReaderWavFile.readMonoAudioFromWavFile(stream); + samples.add(data); + labels.add(dir); + + } + + private byte[] getZipData(InputStream in) throws IOException { + + ByteArrayOutputStream out = new ByteArrayOutputStream(); + byte[] dataBuffer = new byte[1024]; + + int bytesRead; + while ((bytesRead = in.read(dataBuffer, 0, 1024)) != -1) { + out.write(dataBuffer, 0, bytesRead); + } + + return out.toByteArray(); + } + + private byte[] getZipData(URL url) throws IOException { + InputStream in = new BufferedInputStream(url.openStream()); + return getZipData(in); + } + +} diff --git a/src/test/java/org/apache/sysds/test/component/matrix/FourierTestData.py b/src/test/java/org/apache/sysds/test/component/matrix/FourierTestData.py new file mode 100644 index 00000000000..371b837f6bb --- /dev/null +++ b/src/test/java/org/apache/sysds/test/component/matrix/FourierTestData.py @@ -0,0 +1,214 @@ +""" +Licensed to the Apache Software Foundation (ASF) under one +or more contributor license agreements. See the NOTICE file +distributed with this work for additional information +regarding copyright ownership. The ASF licenses this file +to you under the Apache License, Version 2.0 (the +"License"); you may not use this file except in compliance +with the License. You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, +software distributed under the License is distributed on an +"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +KIND, either express or implied. See the License for the +specific language governing permissions and limitations +under the License. +""" + +import numpy as np +import csv +import time + +def generate_inputs(num_inputs, max_power): + for _ in range(num_inputs): + power = np.random.randint(1, max_power+1) + length = 2 ** power + yield np.random.rand(length) # generate array of random floats + +def generate_complex_inputs(num_inputs, max_power): + for _ in range(num_inputs): + power = np.random.randint(1, max_power+1) + length = 2 ** power + real_part = np.random.rand(length) + imag_part = np.random.rand(length) + complex_array = real_part + 1j * imag_part + yield complex_array + + +def compute_fft(inputs): + total_time = 0 + num_calculations = 0 + + for input_array in inputs: + start_time = time.time() + result = np.fft.fft(input_array) + end_time = time.time() + + total_time += end_time - start_time + num_calculations += 1 + + if num_calculations % 1000 == 0: + average_time = total_time / num_calculations + print(f"Average execution time after {num_calculations} calculations: {average_time:.6f} seconds") + + yield result + +def compute_ifft(inputs): + total_time = 0 + num_calculations = 0 + + for input_array in inputs: + start_time = time.time() + result = np.fft.ifft(input_array) + end_time = time.time() + + total_time += end_time - start_time + num_calculations += 1 + + if num_calculations % 1000 == 0: + average_time = total_time / num_calculations + print(f"Average execution time after {num_calculations} calculations: {average_time:.6f} seconds") + + yield result + + + +def save_to_file(inputs, outputs, filename, mode='a'): + with open(filename, mode, newline='') as file: + writer = csv.writer(file) + for input_array, output_array in zip(inputs, outputs): + flattened_data = np.concatenate((input_array, output_array.real, output_array.imag)) + writer.writerow(flattened_data) + + +def save_to_file_complex(inputs, outputs, filename, mode='a'): + with open(filename, mode, newline='') as file: + writer = csv.writer(file) + for input_array, output_array in zip(inputs, outputs): + flattened_data = np.concatenate((input_array.real, input_array.imag, output_array.real, output_array.imag)) + writer.writerow(flattened_data) + +def generate_complex_inputs_2d(num_inputs, max_power): + for _ in range(num_inputs): + power = np.random.randint(1, max_power+1) + rows = 2 ** power + cols = 2 ** power + real_part = np.random.rand(rows, cols) + imag_part = np.random.rand(rows, cols) + complex_array = real_part + 1j * imag_part + yield complex_array + +def compute_fft_2d(inputs): + + total_time = 0 + num_calculations = 0 + + for input_array in inputs: + start_time = time.time() + result = np.fft.fft2(input_array) + end_time = time.time() + + total_time += end_time - start_time + num_calculations += 1 + + if num_calculations % 1000 == 0: + average_time = total_time / num_calculations + print(f"Average execution time after {num_calculations} calculations: {average_time:.6f} seconds") + + yield result + +def compute_ifft_2d(inputs): + + total_time = 0 + num_calculations = 0 + + for input_array in inputs: + start_time = time.time() + result = np.fft.ifft2(input_array) + end_time = time.time() + + total_time += end_time - start_time + num_calculations += 1 + + if num_calculations % 1000 == 0: + average_time = total_time / num_calculations + print(f"Average execution time after {num_calculations} calculations: {average_time:.6f} seconds") + + yield result + +def save_to_file_complex_2d(inputs, outputs, filename, mode='a'): + with open(filename, mode, newline='') as file: + writer = csv.writer(file) + for input_array, output_array in zip(inputs, outputs): + flattened_input = np.concatenate((input_array.real.flatten(), input_array.imag.flatten())) + flattened_output = np.concatenate((output_array.real.flatten(), output_array.imag.flatten())) + writer.writerow(np.concatenate((flattened_input, flattened_output))) + + +# Parameters +num_inputs = 100000 +batch_size = 10000 +max_power = 10 # example max power of 2 for input length + + +# Process and save in batches +for i in range(0, num_inputs, batch_size): + current_batch = min(batch_size, num_inputs - i) + inputs = list(generate_inputs(current_batch, max_power)) + outputs = list(compute_fft(inputs)) + save_to_file(inputs, outputs, "fft_data.csv", mode='a' if i > 0 else 'w') + print(f"Batch {i//batch_size + 1} out of {num_inputs//batch_size} saved to fft_data.csv") + +print("All data for fft processed and saved.") + +# Process and save in batches + + +for i in range(0, num_inputs, batch_size): + current_batch = min(batch_size, num_inputs - i) + inputs = list(generate_inputs(current_batch, max_power)) + outputs = list(compute_ifft(inputs)) + save_to_file(inputs, outputs, "ifft_data.csv", mode='a' if i > 0 else 'w') + print(f"Batch {i//batch_size + 1} out of {num_inputs//batch_size} saved to ifft_data.csv") + +print("All real iff data processed and saved.") + +# Process and save in batches +for i in range(0, num_inputs, batch_size): + current_batch = min(batch_size, num_inputs - i) + inputs = list(generate_complex_inputs(current_batch, max_power)) + outputs = list(compute_ifft(inputs)) + save_to_file_complex(inputs, outputs, "complex_ifft_data.csv", mode='a' if i > 0 else 'w') + print(f"Batch {i//batch_size + 1} out of {num_inputs//batch_size} saved to complex_ifft_data.csv") + +print("All complex ifft data processed and saved.") + + +num_inputs = 100000 +batch_size = 1000 +max_power = 3 + +# Process and save 2D FFT data in batches +filename_2d = "complex_fft_2d_data.csv" +for i in range(0, num_inputs, batch_size): + current_batch = min(batch_size, num_inputs - i) + inputs_2d = list(generate_complex_inputs_2d(current_batch, max_power)) + outputs_2d = list(compute_fft_2d(inputs_2d)) + save_to_file_complex_2d(inputs_2d, outputs_2d, filename_2d, mode='a' if i > 0 else 'w') + print(f"Batch {i//batch_size + 1} out of {num_inputs//batch_size} saved to {filename_2d}") + +print("All complex 2D fft data processed and saved.") + +# Process and save 2D IFFT data in batches +filename_2d = "complex_ifft_2d_data.csv" +for i in range(0, num_inputs, batch_size): + current_batch = min(batch_size, num_inputs - i) + inputs_2d = list(generate_complex_inputs_2d(current_batch, max_power)) + outputs_2d = list(compute_ifft_2d(inputs_2d)) + save_to_file_complex_2d(inputs_2d, outputs_2d, filename_2d, mode='a' if i > 0 else 'w') + print(f"Batch {i//batch_size + 1} out of {num_inputs//batch_size} saved to {filename_2d}") + +print("All complex 2D ifft data processed and saved.") + diff --git a/src/test/java/org/apache/sysds/test/component/matrix/FourierTestWithFiles.java b/src/test/java/org/apache/sysds/test/component/matrix/FourierTestWithFiles.java new file mode 100644 index 00000000000..fe051e2c272 --- /dev/null +++ b/src/test/java/org/apache/sysds/test/component/matrix/FourierTestWithFiles.java @@ -0,0 +1,453 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + +package org.apache.sysds.test.component.matrix; + +import org.junit.Test; +import java.io.IOException; +import java.io.BufferedReader; +import java.io.FileReader; + +import static org.junit.Assert.assertTrue; +import static org.apache.sysds.runtime.matrix.data.LibMatrixFourier.fft; +import static org.apache.sysds.runtime.matrix.data.LibMatrixFourier.ifft; + +public class FourierTestWithFiles { + int progressInterval = 5000; + + // prior to executing the following tests it is necessary to run the Numpy Script in FourierTestData.py + // and add the generated files to the root of the project. + @Test + public void testFftWithNumpyData() throws IOException { + + String filename = "fft_data.csv"; // Path to your CSV file + String path = "./src/test/java/org/apache/sysds/test/component/matrix/"; + + BufferedReader reader = new BufferedReader(new FileReader(path+filename)); + String line; + int lineNumber = 0; + long totalTime = 0; // Total time for all FFT computations + int numCalculations = 0; // Number of FFT computations + + while ((line = reader.readLine()) != null) { + lineNumber++; + + String[] values = line.split(","); + int n = values.length / 3; + + double[] re = new double[n]; + double[] im = new double[n]; + + double[][] expected = new double[2][n]; // First row for real, second row for imaginary parts + + for (int i = 0; i < n; i++) { + re[i] = Double.parseDouble(values[i]); + expected[0][i] = Double.parseDouble(values[n + i]); // Real part + expected[1][i] = Double.parseDouble(values[n * 2 + i]); // Imaginary part + } + + long startTime = System.nanoTime(); + fft(re, im, 1, n); + long endTime = System.nanoTime(); + + if(lineNumber > 1000){ + totalTime += (endTime - startTime); + numCalculations++; + + if (numCalculations % progressInterval == 0) { + double averageTime = (totalTime / 1e6) / numCalculations; // Average time in milliseconds + System.out.println("fft(double[][][] in): Average execution time after " + numCalculations + " calculations: " + String.format("%.8f", averageTime/1000) + " s"); + } + } + + double[][] actual = {re, im}; + + // Validate the FFT results + validateFftResults(expected, actual, lineNumber); + } + + reader.close(); + } + + private void validateFftResults(double[][] expected, double[][] actual, int lineNumber) { + + int length = expected[0].length; + + for (int i = 0; i < length; i++) { + double realActual = actual[0][i]; + double imagActual = actual[1][i]; + assertEquals("Mismatch in real part at index " + i + " in line " + lineNumber, expected[0][i], realActual, 1e-9); + assertEquals("Mismatch in imaginary part at index " + i + " in line " + lineNumber, expected[1][i], imagActual, 1e-9); + } + + if(lineNumber % progressInterval == 0){ + System.out.println("fft(double[][][] in): Finished processing line " + lineNumber); + } + + } + + @Test + public void testFftExecutionTime() throws IOException { + + String filename = "fft_data.csv"; // Path to your CSV file + String path = "./src/test/java/org/apache/sysds/test/component/matrix/"; + + BufferedReader reader = new BufferedReader(new FileReader(path+filename)); + String line; + int lineNumber = 0; + long totalTime = 0; // Total time for all FFT computations + int numCalculations = 0; // Number of FFT computations + + while ((line = reader.readLine()) != null) { + + lineNumber++; + String[] values = line.split(","); + int n = values.length / 3; + + double[] re = new double[n]; + double[] im = new double[n]; + + for (int i = 0; i < n; i++) { + re[i] = Double.parseDouble(values[i]); // Real part + im[i] = Double.parseDouble(values[n + i]); // Imaginary part + } + + long startTime = System.nanoTime(); + + fft(re, im, 1, n); + + long endTime = System.nanoTime(); + + if(lineNumber > 1000){ + totalTime += (endTime - startTime); + numCalculations++; + + if (numCalculations % progressInterval == 0) { + double averageTime = (totalTime / 1e6) / numCalculations; // Average time in milliseconds + System.out.println("fft_old(double[][][] in, boolean calcInv) Average execution time after " + numCalculations + " calculations: " + String.format("%.8f", averageTime/1000) + " s"); + } + } + } + + reader.close(); + } + + @Test + public void testFftExecutionTimeOfOneDimFFT() throws IOException { + + String filename = "fft_data.csv"; // Path to your CSV file + String path = "./src/test/java/org/apache/sysds/test/component/matrix/"; + + BufferedReader reader = new BufferedReader(new FileReader(path+filename)); + String line; + int lineNumber = 0; + long totalTime = 0; // Total time for all FFT computations + int numCalculations = 0; // Number of FFT computations + + while ((line = reader.readLine()) != null) { + + lineNumber++; + String[] values = line.split(","); + int n = values.length / 2; + + double[] re = new double[n]; + double[] im = new double[n]; + + for (int i = 0; i < n; i++) { + re[i] = Double.parseDouble(values[i]); // Real part + im[i] = Double.parseDouble(values[n + i]); // Imaginary part + } + + long startTime = System.nanoTime(); + // one dimensional + fft(re, im, 1, n); + + long endTime = System.nanoTime(); + + if(lineNumber > 1000){ + totalTime += (endTime - startTime); + numCalculations++; + + if (numCalculations % progressInterval == 0) { + double averageTime = (totalTime / 1e6) / numCalculations; // Average time in milliseconds + System.out.println("fft_one_dim: Average execution time after " + numCalculations + " calculations: " + String.format("%.8f", averageTime/1000) + " s "); + } + } + } + + reader.close(); + } + + // prior to executing this test it is necessary to run the Numpy Script in FourierTestData.py and add the generated file to the root of the project. + @Test + public void testIfftWithRealNumpyData() throws IOException { + + String filename = "ifft_data.csv"; // Path to your CSV file + String path = "./src/test/java/org/apache/sysds/test/component/matrix/"; + + BufferedReader reader = new BufferedReader(new FileReader(path+filename)); + String line; + int lineNumber = 0; + + while ((line = reader.readLine()) != null) { + + lineNumber++; + String[] values = line.split(","); + int n = values.length / 3; + + double[] re = new double[n]; + double[] im = new double[n]; + + double[][] expected = new double[2][n]; // First row for real, second row for imaginary parts + + for (int i = 0; i < n; i++) { + re[i] = Double.parseDouble(values[i]); // Real part of input + // Imaginary part of input is assumed to be 0 + expected[0][i] = Double.parseDouble(values[n + i]); // Real part of expected output + expected[1][i] = Double.parseDouble(values[n * 2 + i]); // Imaginary part of expected output + } + + ifft(re, im, 1, n); // Perform IFFT + + double[][] actual = new double[][]{re, im}; + // Validate the IFFT results + validateFftResults(expected, actual, lineNumber); + } + + reader.close(); + + } + + @Test + public void testIfftWithComplexNumpyData() throws IOException { + + String filename = "complex_ifft_data.csv"; // Adjusted path to your IFFT data file with complex inputs + String path = "./src/test/java/org/apache/sysds/test/component/matrix/"; + + BufferedReader reader = new BufferedReader(new FileReader(path+filename)); + String line; + int lineNumber = 0; + long totalTime = 0; // Total time for all IFFT computations + int numCalculations = 0; // Number of IFFT computations + + while ((line = reader.readLine()) != null) { + lineNumber++; + String[] values = line.split(","); + int n = values.length / 4; // Adjusted for complex numbers + + // Real and imaginary parts + double[] re = new double[n]; + double[] im = new double[n]; + + double[][] expected = new double[2][n]; // Expected real and imaginary parts + + for (int i = 0; i < n; i++) { + re[i] = Double.parseDouble(values[i]); // Real part of input + im[i] = Double.parseDouble(values[i + n]); // Imaginary part of input + expected[0][i] = Double.parseDouble(values[i + 2 * n]); // Expected real part + expected[1][i] = Double.parseDouble(values[i + 3 * n]); // Expected imaginary part + } + + long startTime = System.nanoTime(); + + ifft(re, im, 1, n); // Perform IFFT + + long endTime = System.nanoTime(); + + if (lineNumber > 1000) { + totalTime += (endTime - startTime); + numCalculations++; + } + + double[][] actual = new double[][]{re, im}; + // Validate the IFFT results + validateComplexIFftResults(expected, actual, lineNumber); + + if (lineNumber % progressInterval == 0) { + double averageTime = (totalTime / 1e6) / numCalculations; // Average time in milliseconds + System.out.println("ifft: Average execution time after " + numCalculations + " calculations: " + String.format("%.8f", averageTime / 1000) + " s"); + } + } + + reader.close(); + } + + private void validateComplexIFftResults(double[][] expected, double[][] actual, int lineNumber) { + + int length = expected[0].length; + + for (int i = 0; i < length; i++) { + double realActual = actual[0][i]; + double imagActual = actual[1][i]; + assertEquals("Mismatch in real part at index " + i + " in line " + lineNumber, expected[0][i], realActual, 1e-9); + assertEquals("Mismatch in imaginary part at index " + i + " in line " + lineNumber, expected[1][i], imagActual, 1e-9); + } + + if (lineNumber % progressInterval == 0) { + System.out.println("ifft(complex input): Finished processing line " + lineNumber); + } + + } + + @Test + public void testFft2dWithNumpyData() throws IOException { + + String filename = "complex_fft_2d_data.csv"; // path to your 2D FFT data file + String path = "./src/test/java/org/apache/sysds/test/component/matrix/"; + + BufferedReader reader = new BufferedReader(new FileReader(path+filename)); + String line; + int lineNumber = 0; + long totalTime = 0; // Total time for all FFT 2D computations + int numCalculations = 0; // Number of FFT 2D computations + + while ((line = reader.readLine()) != null) { + lineNumber++; + String[] values = line.split(","); + int halfLength = values.length / 4; + int sideLength = (int) Math.sqrt(halfLength); // Assuming square matrix + + // Real and imaginary parts + double[] re = new double[sideLength*sideLength]; + double[] im = new double[sideLength*sideLength]; + + double[][][] expected = new double[2][sideLength][sideLength]; + + for (int i = 0; i < halfLength; i++) { + int row = i / sideLength; + int col = i % sideLength; + + // i == row*sideLength+col?! + re[row*sideLength+col] = Double.parseDouble(values[i]); + im[row*sideLength+col] = Double.parseDouble(values[i + halfLength]); + expected[0][row][col] = Double.parseDouble(values[i + 2 * halfLength]); + expected[1][row][col] = Double.parseDouble(values[i + 3 * halfLength]); + } + + long startTime = System.nanoTime(); + // Use your fft2d implementation + fft(re, im, sideLength, sideLength); + //double[][][] javaFftResult = fft_old(input, false); // Use your fft2d implementation + long endTime = System.nanoTime(); + totalTime += (endTime - startTime); + numCalculations++; + + for (int i = 0; i < sideLength; i++) { + for (int j = 0; j < sideLength; j++) { + assertComplexEquals("Mismatch at [" + i + "][" + j + "] in line " + lineNumber, + expected[0][i][j], expected[1][i][j], + re[i*sideLength+j], im[i*sideLength+j]); + } + } + + if (lineNumber % progressInterval == 0) { + double averageTime = (totalTime / 1e6) / numCalculations; // Average time in milliseconds + System.out.println("fft2d: Average execution time after " + numCalculations + " calculations: " + String.format("%.8f", averageTime/1000) + " s"); + } + } + + reader.close(); + System.out.println("fft2d: Finished processing " + lineNumber + " lines.\n Average execution time: " + String.format("%.8f", (totalTime / 1e6 / numCalculations)/1000) + " s"); + + } + + + @Test + public void testIfft2dWithNumpyData() throws IOException { + + String filename = "complex_ifft_2d_data.csv"; // path to your 2D IFFT data file + String path = "./src/test/java/org/apache/sysds/test/component/matrix/"; + + BufferedReader reader = new BufferedReader(new FileReader(path+filename)); + String line; + int lineNumber = 0; + long totalTime = 0; // Total time for all IFFT 2D computations + int numCalculations = 0; // Number of IFFT 2D computations + + while ((line = reader.readLine()) != null) { + + lineNumber++; + String[] values = line.split(","); + int halfLength = values.length / 4; + int sideLength = (int) Math.sqrt(halfLength); // Assuming square matrix + + // Real and imaginary parts + double[] re = new double[sideLength*sideLength]; + double[] im = new double[sideLength*sideLength]; + + double[][][] expected = new double[2][sideLength][sideLength]; + + for (int i = 0; i < halfLength; i++) { + int row = i / sideLength; + int col = i % sideLength; + + re[row*sideLength+col] = Double.parseDouble(values[i]); + im[row*sideLength+col] = Double.parseDouble(values[i + halfLength]); + + expected[0][row][col] = Double.parseDouble(values[i + 2 * halfLength]); + expected[1][row][col] = Double.parseDouble(values[i + 3 * halfLength]); + } + + long startTime = System.nanoTime(); + + // Use your ifft2d implementation + ifft(re, im, sideLength, sideLength); + + long endTime = System.nanoTime(); + if(lineNumber > 1000){ + totalTime += (endTime - startTime); + numCalculations++; + } + + for (int i = 0; i < sideLength; i++) { + for (int j = 0; j < sideLength; j++) { + assertComplexEquals("Mismatch at [" + i + "][" + j + "] in line " + lineNumber, + expected[0][i][j], expected[1][i][j], + re[i*sideLength+j], im[i*sideLength+j]); + } + } + + if (lineNumber % progressInterval == 0) { + System.out.println("fft2d/ifft2d: Finished processing line " + lineNumber); + double averageTime = (totalTime / 1e6) / numCalculations; // Average time in milliseconds + System.out.println("Ifft2d Average execution time after " + numCalculations + " calculations: " + String.format("%.8f", averageTime/1000) + " s"); + } + } + + reader.close(); + System.out.println("ifft2d: Finished processing " + lineNumber + " lines.\n Average execution time: " + String.format("%.8f", (totalTime / 1e6 / numCalculations)/1000) + " ms"); + + } + + // Helper method for asserting equality with a tolerance + private static void assertEquals(String message, double expected, double actual, double tolerance) { + assertTrue(message + " - Expected: " + expected + ", Actual: " + actual, Math.abs(expected - actual) <= tolerance); + } + + private void assertComplexEquals(String message, double expectedReal, double expectedImag, double actualReal, double actualImag) { + + final double EPSILON = 1e-9; + assertTrue(message + " - Mismatch in real part. Expected: " + expectedReal + ", Actual: " + actualReal, + Math.abs(expectedReal - actualReal) <= EPSILON); + assertTrue(message + " - Mismatch in imaginary part. Expected: " + expectedImag + ", Actual: " + actualImag, + Math.abs(expectedImag - actualImag) <= EPSILON); + + } + +} diff --git a/src/test/java/org/apache/sysds/test/component/matrix/LibMatrixSTFTTest.java b/src/test/java/org/apache/sysds/test/component/matrix/LibMatrixSTFTTest.java index 4f46576c075..e2ed4a622c0 100644 --- a/src/test/java/org/apache/sysds/test/component/matrix/LibMatrixSTFTTest.java +++ b/src/test/java/org/apache/sysds/test/component/matrix/LibMatrixSTFTTest.java @@ -28,90 +28,77 @@ public class LibMatrixSTFTTest { - @Test - public void simple_test() { + @Test + public void simple_test() { - double[] signal = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}; - int windowSize = 4; - int overlap = 2; - MatrixBlock[] res = one_dim_stft(signal, windowSize, overlap); - double[] res_re = res[0].getDenseBlockValues(); - double[] res_im = res[1].getDenseBlockValues(); + double[] signal = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}; + int windowSize = 4; + int overlap = 2; + double[][] stftResult = one_dim_stft(signal, windowSize, overlap); + // 1st row real part, 2nd row imaginary part + double[][] expected = {{6, -2, -2, -2, 14, -2, -2, -2, 22, -2, -2, -2, 30, -2, -2, -2, 38, -2, -2, -2, 46, -2, -2, -2, 54, -2, -2, -2},{0, 2, 0, -2, 0, 2, 0, -2, 0, 2, 0, -2, 0, 2, 0, -2, 0, 2, 0, -2, 0, 2, 0, -2, 0, 2, 0, -2}}; - double[]expected_re = {6, -2, -2, -2, 14, -2, -2, -2, 22, -2, -2, -2, 30, -2, -2, -2, 38, -2, -2, -2, 46, -2, -2, -2, 54, -2, -2, -2}; + for(double[] row : stftResult){ + for (double elem : row){ + System.out.print(elem + " "); + } + System.out.println(); + } - double[]expected_im = {0, 2, 0, -2, 0, 2, 0, -2, 0, 2, 0, -2, 0, 2, 0, -2, 0, 2, 0, -2, 0, 2, 0, -2, 0, 2, 0, -2}; + assertArrayEquals(expected[0], stftResult[0], 0.0001); + assertArrayEquals(expected[1], stftResult[1], 0.0001); - /* - for(double[] row : stftResult){ - for (double elem : row){ - System.out.print(elem + " "); - } - System.out.println(); - } - */ - for(double elem : res_re){ - System.out.print(elem+" "); - } - System.out.println(); - for(double elem : res_im){ - System.out.print(elem+" "); - } + } - assertArrayEquals(expected_re, res_re, 0.0001); - assertArrayEquals(expected_im, res_im, 0.0001); + @Test + public void matrix_block_one_dim_test(){ - } + double[] in = {0, 18, -15, 3}; - @Test - public void matrix_block_one_dim_test(){ + double[] expected_re = {6,15,-36,15}; + double[] expected_im = {0,-15,0,15}; - double[] in = {0, 18, -15, 3}; + MatrixBlock[] res = stft(in, 4, 0); + double[] res_re = res[0].getDenseBlockValues(); + double[] res_im = res[1].getDenseBlockValues(); - double[] expected_re = {6,15,-36,15}; - double[] expected_im = {0,-15,0,15}; + for(double elem : res_re){ + System.out.print(elem+" "); + } + System.out.println(); + for(double elem : res_im){ + System.out.print(elem+" "); + } - MatrixBlock[] res = stft(in, 4, 0); - double[] res_re = res[0].getDenseBlockValues(); - double[] res_im = res[1].getDenseBlockValues(); + assertArrayEquals(expected_re, res_re, 0.0001); + assertArrayEquals(expected_im, res_im, 0.0001); - for(double elem : res_re){ - System.out.print(elem+" "); - } - System.out.println(); - for(double elem : res_im){ - System.out.print(elem+" "); - } + } - assertArrayEquals(expected_re, res_re, 0.0001); - assertArrayEquals(expected_im, res_im, 0.0001); + @Test + public void matrix_block_one_dim_test2(){ - } + double[] in = {10, 5, -3, 8, 15, -6, 2, 0}; - @Test - public void matrix_block_one_dim_test2(){ + double[] expected_re = {20.0, 13.0, -6.0, 13.0, 14.0, -18.0, 10.0, -18.0, 11.0, 13.0, 23.0, 13.0}; + double[] expected_im = {0.0, 3.0, 0.0, -3.0, 0.0, -14.0, 0.0, 14.0, 0.0, 6.0, 0.0, -6.0 }; - double[] in = {10, 5, -3, 8, 15, -6, 2, 0}; + MatrixBlock[] res = stft(in, 4, 2); + double[] res_re = res[0].getDenseBlockValues(); + double[] res_im = res[1].getDenseBlockValues(); - double[] expected_re = {20.0, 13.0, -6.0, 13.0, 14.0, -18.0, 10.0, -18.0, 11.0, 13.0, 23.0, 13.0}; - double[] expected_im = {0.0, 3.0, 0.0, -3.0, 0.0, -14.0, 0.0, 14.0, 0.0, 6.0, 0.0, -6.0 }; + for(double elem : res_re){ + System.out.print(elem+" "); + } + System.out.println(); + for(double elem : res_im){ + System.out.print(elem+" "); + } - MatrixBlock[] res = stft(in, 4, 2); - double[] res_re = res[0].getDenseBlockValues(); - double[] res_im = res[1].getDenseBlockValues(); + assertArrayEquals(expected_re, res_re, 0.0001); + assertArrayEquals(expected_im, res_im, 0.0001); - for(double elem : res_re){ - System.out.print(elem+" "); - } - System.out.println(); - for(double elem : res_im){ - System.out.print(elem+" "); - } + } - assertArrayEquals(expected_re, res_re, 0.0001); - assertArrayEquals(expected_im, res_im, 0.0001); - - } - -} \ No newline at end of file +} From 2c6fb3a4c1a93ac1f2ebabf36466f5bcf8e0ca37 Mon Sep 17 00:00:00 2001 From: Jessica Priebe Date: Tue, 16 Jan 2024 19:10:48 +0100 Subject: [PATCH 094/133] added ReaderWavFile, LibMatrixKeywordSpotting --- .../sysds/runtime/io/ReaderWavFile.java | 160 ++++----- .../matrix/data/LibMatrixKeywordSpotting.java | 314 ++++++++---------- 2 files changed, 205 insertions(+), 269 deletions(-) diff --git a/src/main/java/org/apache/sysds/runtime/io/ReaderWavFile.java b/src/main/java/org/apache/sysds/runtime/io/ReaderWavFile.java index 95966d6bff5..2b1e689afc2 100644 --- a/src/main/java/org/apache/sysds/runtime/io/ReaderWavFile.java +++ b/src/main/java/org/apache/sysds/runtime/io/ReaderWavFile.java @@ -1,102 +1,76 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - package org.apache.sysds.runtime.io; +import javax.sound.sampled.*; import java.io.File; import java.io.IOException; import java.io.InputStream; -import javax.sound.sampled.AudioSystem; -import javax.sound.sampled.AudioFormat; -import javax.sound.sampled.AudioInputStream; -import javax.sound.sampled.UnsupportedAudioFileException; - public class ReaderWavFile { - public static double[] readMonoAudioFromWavFile(String filePath) { - - try { - // open audio file - AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(new File(filePath)); - double[] audioValues = readMonoAudioFromWavFile(audioInputStream); - audioInputStream.close(); - return audioValues; - - } catch (UnsupportedAudioFileException | IOException e) { - e.printStackTrace(); - return null; - } - - } - - public static double[] readMonoAudioFromWavFile(InputStream inputStream) { - - try { - // open audio file - AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(inputStream); - - // collapse channels to mono channel - int channels = 1; - AudioFormat monoAudioFormat = new AudioFormat( - audioInputStream.getFormat().getSampleRate(), - audioInputStream.getFormat().getSampleSizeInBits(), - channels, - true, - false - ); - AudioInputStream monoAudioInputStream = AudioSystem.getAudioInputStream(monoAudioFormat, audioInputStream); - - // curation of audio - int numFrames = (int) monoAudioInputStream.getFrameLength(); - // size of one frame in bytes - int frameSize = monoAudioInputStream.getFormat().getFrameSize(); - - // read audio into buffer - byte[] audioData = new byte[numFrames * frameSize]; - int bytesRead = audioInputStream.read(audioData); - - // read operation failed - if (bytesRead == -1) { - return null; - } - - // convert byte array to double array - double[] audioValues = new double[numFrames]; - for (int i = 0, frameIndex = 0; i < bytesRead; i += frameSize, frameIndex++) { - // 16-bit PCM encoding - // combine two bytes into a 16-bit integer (short) - short sampleValue = (short) ((audioData[i + 1] << 8) | (audioData[i] & 0xFF)); - // audio ranges from -32768 to 32767, normalize to range -1 to 1 - audioValues[frameIndex] = sampleValue / 32768.0; - } - - // close audio streams - monoAudioInputStream.close(); - audioInputStream.close(); - return audioValues; - - } catch (UnsupportedAudioFileException | IOException e) { - e.printStackTrace(); - return null; - } - - } - + public static double[] readMonoFromWavFile(String filePath) { + try { + // open audio file + AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(new File(filePath)); + double[] audioValues = readMonoFromWavFile(audioInputStream); + audioInputStream.close(); + return audioValues; + + } catch (UnsupportedAudioFileException | IOException e) { + e.printStackTrace(); + return null; + } + } + + public static double[] readMonoFromWavFile(InputStream inputStream) { + + try { + // open audio file + AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(inputStream); + + // collapse channels to mono channel + int channels = 1; + AudioFormat monoAudioFormat = new AudioFormat( + audioInputStream.getFormat().getSampleRate(), + audioInputStream.getFormat().getSampleSizeInBits(), + channels, + true, + false + ); + AudioInputStream monoAudioInputStream = AudioSystem.getAudioInputStream(monoAudioFormat, audioInputStream); + + // curation of audio + int numFrames = (int) monoAudioInputStream.getFrameLength(); + // size of one frame in bytes + int frameSize = monoAudioInputStream.getFormat().getFrameSize(); + + // read audio into buffer + byte[] audioData = new byte[numFrames * frameSize]; + int bytesRead = audioInputStream.read(audioData); + + // read operation failed + if (bytesRead == -1) { + return null; + } + + // convert byte array to double array + double[] audioValues = new double[numFrames]; + for (int i = 0, frameIndex = 0; i < bytesRead; i += frameSize, frameIndex++) { + // 16-bit PCM encoding + // combine two bytes into a 16-bit integer (short) + short sampleValue = (short) ((audioData[i + 1] << 8) | (audioData[i] & 0xFF)); + // audio ranges from -32768 to 32767, normalize to range -1 to 1 + audioValues[frameIndex] = sampleValue / 32768.0; + } + + // close audio streams + monoAudioInputStream.close(); + audioInputStream.close(); + + return audioValues; + + } catch (UnsupportedAudioFileException | IOException e) { + e.printStackTrace(); + return null; + } + } } diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java index 33d8adef302..1aae952caaa 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java @@ -1,193 +1,155 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - package org.apache.sysds.runtime.matrix.data; import org.apache.sysds.runtime.io.ReaderWavFile; +import java.io.*; import java.net.URL; - -import java.io.InputStream; -import java.io.FileInputStream; -import java.io.BufferedInputStream; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; -import java.io.IOException; - -import java.util.List; -import java.util.ArrayList; -import java.util.Set; -import java.util.HashSet; +import java.util.*; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; public class LibMatrixKeywordSpotting { - List samples = new ArrayList<>(); - List labels = new ArrayList<>(); - - public LibMatrixKeywordSpotting() { - - // load all data - // data: http://storage.googleapis.com/download.tensorflow.org/data/mini_speech_commands.zip - // zip contains command folders which contain corresponding .wav files - // maybe change label to int? - loadAllData(); - - // convert waveforms to magnitudes of spectrogram - // uses stft - for (int i = 0; i < samples.size(); i++){ - double[] wave = samples.get(i); - double[] magnitudes = convertWaveToMagnitudesSpectrogram(wave); - samples.set(i, magnitudes); - } - - // TODO: - // train model - // use gaussianClassifier??? - // [prior, means, covs, det] = gaussianClassifier(D=X, C=y, varSmoothing=$2); - // use global variables for classifier - } - - private double[] convertWaveToMagnitudesSpectrogram(double[] wave){ - - // length=255, overlap=128 - // TODO: adjust stft - double[][] spectrogram = LibMatrixSTFT.one_dim_stft(wave, 255, 128); - - int cols = spectrogram[0].length; - double[] magnitudes = new double[cols]; - for (int i = 0; i < cols; i++){ - magnitudes[i] = Math.sqrt(Math.pow(spectrogram[0][i], 2) + Math.pow(spectrogram[0][i], 2)); - } - - return magnitudes; - } - - public String predictCommandForFile(String filePath){ - - // read wave file - double[] wave = ReaderWavFile.readMonoAudioFromWavFile(filePath); - - // convert waveforms to spectrogram - double[] magnitudes = convertWaveToMagnitudesSpectrogram(wave); - - // use global variables for prediction - // TODO - - return null; - } - - private void loadAllData(){ - - // doesn't work for url - // String url = "http://storage.googleapis.com/download.tensorflow.org/data/mini_speech_commands.zip"; - // Set dirs = Set.of("yes", "no"); - - String zipFilePath = "/Users/jessica/desktop/mini_speech_commands.zip"; - - try { - // get zip data - byte[] zipData = getZipData(new FileInputStream(zipFilePath)); - - // get folder names - Set dirs = getDirectories(zipData); - - for (String dir : dirs) { - readWavFilesDirectory(zipData, dir); - } - - } catch (IOException e) { - e.printStackTrace(); - } - - } - - private Set getDirectories(byte[] zipData) throws IOException { - - Set dirs = new HashSet<>(); - ZipInputStream stream = new ZipInputStream(new ByteArrayInputStream(zipData)); - - // exclude main directory - ZipEntry entry = stream.getNextEntry(); - int mainDirLength = entry.getName().length(); - - while ((entry = stream.getNextEntry()) != null) { - if (entry.isDirectory()) { - String dir = entry.getName(); - // remove / at the end - dirs.add(dir.substring(mainDirLength, dir.length() - 1)); - } - } - - return dirs; - } - - private void readWavFilesDirectory(byte[] zipData, String dir) throws IOException { - - ZipInputStream stream = new ZipInputStream(new ByteArrayInputStream(zipData)); - ZipEntry entry; - - while ((entry = stream.getNextEntry()) != null) { - if (entry.getName().startsWith(dir) && entry.isDirectory()) { - readWavFilesDirectory(stream, dir); - // dont read next dir - break; - } - } - - } - - private void readWavFilesDirectory(ZipInputStream stream, String dir) throws IOException { - - ZipEntry entry; - while ((entry = stream.getNextEntry()) != null && !entry.isDirectory() && entry.getName().endsWith(".wav")) { - readWavFile(entry, dir); - } - - } - - private void readWavFile(ZipEntry entry, String dir) { - - InputStream stream = new ByteArrayInputStream(entry.getExtra()); - double[] data = ReaderWavFile.readMonoAudioFromWavFile(stream); - samples.add(data); - labels.add(dir); - - } + List samples = new ArrayList<>(); + List labels = new ArrayList<>(); - private byte[] getZipData(InputStream in) throws IOException { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - byte[] dataBuffer = new byte[1024]; - int bytesRead; - while ((bytesRead = in.read(dataBuffer, 0, 1024)) != -1) { - out.write(dataBuffer, 0, bytesRead); - } + public LibMatrixKeywordSpotting() { - return out.toByteArray(); - } + // load all data + // data: http://storage.googleapis.com/download.tensorflow.org/data/mini_speech_commands.zip + // zip contains command folders which contain corresponding .wav files + // maybe change label to int? + loadAllData(); - private byte[] getZipData(URL url) throws IOException { - InputStream in = new BufferedInputStream(url.openStream()); - return getZipData(in); - } + // convert waveforms to magnitudes of spectrogram + // uses stft + for (int i = 0; i < samples.size(); i++){ + double[] wave = samples.get(i); + /** double[] magnitudes = convertWaveToMagnitudesSpectrogram(wave); + * samples.set(i, magnitudes); + * */ + } + // TODO: + // train model + // use gaussianClassifier/CNN??? + + // [prior, means, covs, det] = gaussianClassifier(D=X, C=y, varSmoothing=$2); + // use global variables for classifier + } + + /** + private double[] convertWaveToMagnitudesSpectrogram(double[] wave){ + // length=255, overlap=128 + double[][] spectrogram = stft(wave, 255, 128); + + int cols = spectrogram[0].length; + double[] magnitudes = new double[cols]; + for (int i = 0; i < cols; i++){ + magnitudes[i] = Math.sqrt(Math.pow(spectrogram[0][i], 2) + Math.pow(spectrogram[0][i], 2)); + } + return magnitudes; + } + */ + + public String getCommandForFile(String filePath){ + + // read wave file + double[] data = ReaderWavFile.readMonoFromWavFile(filePath); + + // convert waveforms to spectrogram + /** double[] magnitudes = convertWaveToMagnitudesSpectrogram(wave); */ + + // use global variables for classifier + // TODO + + return null; + } + + private void loadAllData(){ + + // doesn't work for url + // String url = "http://storage.googleapis.com/download.tensorflow.org/data/mini_speech_commands.zip"; + // Set dirs = Set.of("yes", "no"); + + String zipFilePath = "/Users/jessica/desktop/mini_speech_commands.zip"; + + try { + // get zip data + byte[] zipData = getZipData(new FileInputStream(zipFilePath)); + + // get folder names + Set dirs = getDirectories(zipData); + + for (String dir : dirs) { + readWavFilesDirectory(zipData, dir); + } + + + } catch (IOException e) { + e.printStackTrace(); + } + + } + + private Set getDirectories(byte[] zipData) throws IOException { + Set dirs = new HashSet<>(); + ZipInputStream stream = new ZipInputStream(new ByteArrayInputStream(zipData)); + + // exclude main directory + ZipEntry entry = stream.getNextEntry(); + int mainDirLength = entry.getName().length(); + + while ((entry = stream.getNextEntry()) != null) { + if (entry.isDirectory()) { + String dir = entry.getName(); + // remove / at the end + dirs.add(dir.substring(mainDirLength, dir.length() - 1)); + } + } + return dirs; + } + + private void readWavFilesDirectory(byte[] zipData, String dir) throws IOException { + ZipInputStream stream = new ZipInputStream(new ByteArrayInputStream(zipData)); + ZipEntry entry; + while ((entry = stream.getNextEntry()) != null) { + if (entry.getName().startsWith(dir) && entry.isDirectory()) { + readWavFilesDirectory(stream, dir); + // dont read next dir + break; + } + } + } + + private void readWavFilesDirectory(ZipInputStream stream, String dir) throws IOException { + ZipEntry entry; + while ((entry = stream.getNextEntry()) != null && !entry.isDirectory() && entry.getName().endsWith(".wav")) { + readWavFile(entry, dir); + } + } + + private void readWavFile(ZipEntry entry, String dir) { + InputStream stream = new ByteArrayInputStream(entry.getExtra()); + double[] data = ReaderWavFile.readMonoFromWavFile(stream); + samples.add(data); + labels.add(dir); + } + + private byte[] getZipData(InputStream in) throws IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + byte[] dataBuffer = new byte[1024]; + int bytesRead; + while ((bytesRead = in.read(dataBuffer, 0, 1024)) != -1) { + out.write(dataBuffer, 0, bytesRead); + } + return out.toByteArray(); + } + + private byte[] getZipData(URL url) throws IOException { + InputStream in = new BufferedInputStream(url.openStream()); + return getZipData(in); + } } From fc1a7ec0705456e2e061ce9579586c22042e0166 Mon Sep 17 00:00:00 2001 From: Jessica Priebe Date: Tue, 16 Jan 2024 19:19:09 +0100 Subject: [PATCH 095/133] merged stft into application --- .../matrix/data/LibMatrixKeywordSpotting.java | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java index 1aae952caaa..19bad250b5f 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java @@ -27,9 +27,8 @@ public LibMatrixKeywordSpotting() { // uses stft for (int i = 0; i < samples.size(); i++){ double[] wave = samples.get(i); - /** double[] magnitudes = convertWaveToMagnitudesSpectrogram(wave); - * samples.set(i, magnitudes); - * */ + double[] magnitudes = convertWaveToMagnitudesSpectrogram(wave); + samples.set(i, magnitudes); } // TODO: @@ -40,10 +39,10 @@ public LibMatrixKeywordSpotting() { // use global variables for classifier } - /** + private double[] convertWaveToMagnitudesSpectrogram(double[] wave){ // length=255, overlap=128 - double[][] spectrogram = stft(wave, 255, 128); + double[][] spectrogram = LibMatrixSTFT.one_dim_stft(wave, 255, 128); int cols = spectrogram[0].length; double[] magnitudes = new double[cols]; @@ -52,15 +51,14 @@ private double[] convertWaveToMagnitudesSpectrogram(double[] wave){ } return magnitudes; } - */ public String getCommandForFile(String filePath){ // read wave file - double[] data = ReaderWavFile.readMonoFromWavFile(filePath); + double[] wave = ReaderWavFile.readMonoFromWavFile(filePath); // convert waveforms to spectrogram - /** double[] magnitudes = convertWaveToMagnitudesSpectrogram(wave); */ + double[] magnitudes = convertWaveToMagnitudesSpectrogram(wave); // use global variables for classifier // TODO From a6b4d4e86702c67e9437c94bbb6a6a16d07c69d5 Mon Sep 17 00:00:00 2001 From: Jessica Priebe Date: Tue, 16 Jan 2024 20:51:03 +0100 Subject: [PATCH 096/133] merged stft into application --- .../sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java index 19bad250b5f..55866a7bbad 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java @@ -85,7 +85,6 @@ private void loadAllData(){ readWavFilesDirectory(zipData, dir); } - } catch (IOException e) { e.printStackTrace(); } From f4a651ada27b5f4cb9331851643f15c5f180ce70 Mon Sep 17 00:00:00 2001 From: Jessica Priebe Date: Thu, 18 Jan 2024 14:37:40 +0100 Subject: [PATCH 097/133] tab indentation new files --- .../sysds/runtime/io/ReaderWavFile.java | 110 +++---- .../matrix/data/LibMatrixKeywordSpotting.java | 268 +++++++++--------- 2 files changed, 189 insertions(+), 189 deletions(-) diff --git a/src/main/java/org/apache/sysds/runtime/io/ReaderWavFile.java b/src/main/java/org/apache/sysds/runtime/io/ReaderWavFile.java index 2b1e689afc2..d4295ff10dc 100644 --- a/src/main/java/org/apache/sysds/runtime/io/ReaderWavFile.java +++ b/src/main/java/org/apache/sysds/runtime/io/ReaderWavFile.java @@ -7,70 +7,70 @@ public class ReaderWavFile { - public static double[] readMonoFromWavFile(String filePath) { - try { - // open audio file - AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(new File(filePath)); - double[] audioValues = readMonoFromWavFile(audioInputStream); - audioInputStream.close(); - return audioValues; + public static double[] readMonoFromWavFile(String filePath) { + try { + // open audio file + AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(new File(filePath)); + double[] audioValues = readMonoFromWavFile(audioInputStream); + audioInputStream.close(); + return audioValues; - } catch (UnsupportedAudioFileException | IOException e) { - e.printStackTrace(); - return null; - } - } + } catch (UnsupportedAudioFileException | IOException e) { + e.printStackTrace(); + return null; + } + } - public static double[] readMonoFromWavFile(InputStream inputStream) { + public static double[] readMonoFromWavFile(InputStream inputStream) { - try { - // open audio file - AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(inputStream); + try { + // open audio file + AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(inputStream); - // collapse channels to mono channel - int channels = 1; - AudioFormat monoAudioFormat = new AudioFormat( - audioInputStream.getFormat().getSampleRate(), - audioInputStream.getFormat().getSampleSizeInBits(), - channels, - true, - false - ); - AudioInputStream monoAudioInputStream = AudioSystem.getAudioInputStream(monoAudioFormat, audioInputStream); + // collapse channels to mono channel + int channels = 1; + AudioFormat monoAudioFormat = new AudioFormat( + audioInputStream.getFormat().getSampleRate(), + audioInputStream.getFormat().getSampleSizeInBits(), + channels, + true, + false + ); + AudioInputStream monoAudioInputStream = AudioSystem.getAudioInputStream(monoAudioFormat, audioInputStream); - // curation of audio - int numFrames = (int) monoAudioInputStream.getFrameLength(); - // size of one frame in bytes - int frameSize = monoAudioInputStream.getFormat().getFrameSize(); + // curation of audio + int numFrames = (int) monoAudioInputStream.getFrameLength(); + // size of one frame in bytes + int frameSize = monoAudioInputStream.getFormat().getFrameSize(); - // read audio into buffer - byte[] audioData = new byte[numFrames * frameSize]; - int bytesRead = audioInputStream.read(audioData); + // read audio into buffer + byte[] audioData = new byte[numFrames * frameSize]; + int bytesRead = audioInputStream.read(audioData); - // read operation failed - if (bytesRead == -1) { - return null; - } + // read operation failed + if (bytesRead == -1) { + return null; + } - // convert byte array to double array - double[] audioValues = new double[numFrames]; - for (int i = 0, frameIndex = 0; i < bytesRead; i += frameSize, frameIndex++) { - // 16-bit PCM encoding - // combine two bytes into a 16-bit integer (short) - short sampleValue = (short) ((audioData[i + 1] << 8) | (audioData[i] & 0xFF)); - // audio ranges from -32768 to 32767, normalize to range -1 to 1 - audioValues[frameIndex] = sampleValue / 32768.0; - } + // convert byte array to double array + double[] audioValues = new double[numFrames]; + for (int i = 0, frameIndex = 0; i < bytesRead; i += frameSize, frameIndex++) { + // 16-bit PCM encoding + // combine two bytes into a 16-bit integer (short) + short sampleValue = (short) ((audioData[i + 1] << 8) | (audioData[i] & 0xFF)); + // audio ranges from -32768 to 32767, normalize to range -1 to 1 + audioValues[frameIndex] = sampleValue / 32768.0; + } - // close audio streams - monoAudioInputStream.close(); - audioInputStream.close(); + // close audio streams + monoAudioInputStream.close(); + audioInputStream.close(); - return audioValues; + return audioValues; - } catch (UnsupportedAudioFileException | IOException e) { - e.printStackTrace(); - return null; - } - } + } catch (UnsupportedAudioFileException | IOException e) { + e.printStackTrace(); + return null; + } + } } diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java index 55866a7bbad..0702d771a9e 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java @@ -10,143 +10,143 @@ public class LibMatrixKeywordSpotting { - List samples = new ArrayList<>(); - List labels = new ArrayList<>(); + List samples = new ArrayList<>(); + List labels = new ArrayList<>(); - public LibMatrixKeywordSpotting() { + public LibMatrixKeywordSpotting() { - // load all data - // data: http://storage.googleapis.com/download.tensorflow.org/data/mini_speech_commands.zip - // zip contains command folders which contain corresponding .wav files - // maybe change label to int? - loadAllData(); + // load all data + // data: http://storage.googleapis.com/download.tensorflow.org/data/mini_speech_commands.zip + // zip contains command folders which contain corresponding .wav files + // maybe change label to int? + loadAllData(); - // convert waveforms to magnitudes of spectrogram - // uses stft - for (int i = 0; i < samples.size(); i++){ - double[] wave = samples.get(i); - double[] magnitudes = convertWaveToMagnitudesSpectrogram(wave); - samples.set(i, magnitudes); - } - - // TODO: - // train model - // use gaussianClassifier/CNN??? - - // [prior, means, covs, det] = gaussianClassifier(D=X, C=y, varSmoothing=$2); - // use global variables for classifier - } - - - private double[] convertWaveToMagnitudesSpectrogram(double[] wave){ - // length=255, overlap=128 - double[][] spectrogram = LibMatrixSTFT.one_dim_stft(wave, 255, 128); - - int cols = spectrogram[0].length; - double[] magnitudes = new double[cols]; - for (int i = 0; i < cols; i++){ - magnitudes[i] = Math.sqrt(Math.pow(spectrogram[0][i], 2) + Math.pow(spectrogram[0][i], 2)); - } - return magnitudes; - } - - public String getCommandForFile(String filePath){ - - // read wave file - double[] wave = ReaderWavFile.readMonoFromWavFile(filePath); - - // convert waveforms to spectrogram - double[] magnitudes = convertWaveToMagnitudesSpectrogram(wave); - - // use global variables for classifier - // TODO - - return null; - } - - private void loadAllData(){ - - // doesn't work for url - // String url = "http://storage.googleapis.com/download.tensorflow.org/data/mini_speech_commands.zip"; - // Set dirs = Set.of("yes", "no"); - - String zipFilePath = "/Users/jessica/desktop/mini_speech_commands.zip"; - - try { - // get zip data - byte[] zipData = getZipData(new FileInputStream(zipFilePath)); - - // get folder names - Set dirs = getDirectories(zipData); - - for (String dir : dirs) { - readWavFilesDirectory(zipData, dir); - } - - } catch (IOException e) { - e.printStackTrace(); - } - - } - - private Set getDirectories(byte[] zipData) throws IOException { - Set dirs = new HashSet<>(); - ZipInputStream stream = new ZipInputStream(new ByteArrayInputStream(zipData)); - - // exclude main directory - ZipEntry entry = stream.getNextEntry(); - int mainDirLength = entry.getName().length(); - - while ((entry = stream.getNextEntry()) != null) { - if (entry.isDirectory()) { - String dir = entry.getName(); - // remove / at the end - dirs.add(dir.substring(mainDirLength, dir.length() - 1)); - } - } - return dirs; - } - - private void readWavFilesDirectory(byte[] zipData, String dir) throws IOException { - ZipInputStream stream = new ZipInputStream(new ByteArrayInputStream(zipData)); - ZipEntry entry; - while ((entry = stream.getNextEntry()) != null) { - if (entry.getName().startsWith(dir) && entry.isDirectory()) { - readWavFilesDirectory(stream, dir); - // dont read next dir - break; - } - } - } - - private void readWavFilesDirectory(ZipInputStream stream, String dir) throws IOException { - ZipEntry entry; - while ((entry = stream.getNextEntry()) != null && !entry.isDirectory() && entry.getName().endsWith(".wav")) { - readWavFile(entry, dir); - } - } - - private void readWavFile(ZipEntry entry, String dir) { - InputStream stream = new ByteArrayInputStream(entry.getExtra()); - double[] data = ReaderWavFile.readMonoFromWavFile(stream); - samples.add(data); - labels.add(dir); - } - - private byte[] getZipData(InputStream in) throws IOException { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - byte[] dataBuffer = new byte[1024]; - int bytesRead; - while ((bytesRead = in.read(dataBuffer, 0, 1024)) != -1) { - out.write(dataBuffer, 0, bytesRead); - } - return out.toByteArray(); - } - - private byte[] getZipData(URL url) throws IOException { - InputStream in = new BufferedInputStream(url.openStream()); - return getZipData(in); - } + // convert waveforms to magnitudes of spectrogram + // uses stft + for (int i = 0; i < samples.size(); i++){ + double[] wave = samples.get(i); + double[] magnitudes = convertWaveToMagnitudesSpectrogram(wave); + samples.set(i, magnitudes); + } + + // TODO: + // train model + // use gaussianClassifier/CNN??? + + // [prior, means, covs, det] = gaussianClassifier(D=X, C=y, varSmoothing=$2); + // use global variables for classifier + } + + + private double[] convertWaveToMagnitudesSpectrogram(double[] wave){ + // length=255, overlap=128 + double[][] spectrogram = LibMatrixSTFT.one_dim_stft(wave, 255, 128); + + int cols = spectrogram[0].length; + double[] magnitudes = new double[cols]; + for (int i = 0; i < cols; i++){ + magnitudes[i] = Math.sqrt(Math.pow(spectrogram[0][i], 2) + Math.pow(spectrogram[0][i], 2)); + } + return magnitudes; + } + + public String getCommandForFile(String filePath){ + + // read wave file + double[] wave = ReaderWavFile.readMonoFromWavFile(filePath); + + // convert waveforms to spectrogram + double[] magnitudes = convertWaveToMagnitudesSpectrogram(wave); + + // use global variables for classifier + // TODO + + return null; + } + + private void loadAllData(){ + + // doesn't work for url + // String url = "http://storage.googleapis.com/download.tensorflow.org/data/mini_speech_commands.zip"; + // Set dirs = Set.of("yes", "no"); + + String zipFilePath = "/Users/jessica/desktop/mini_speech_commands.zip"; + + try { + // get zip data + byte[] zipData = getZipData(new FileInputStream(zipFilePath)); + + // get folder names + Set dirs = getDirectories(zipData); + + for (String dir : dirs) { + readWavFilesDirectory(zipData, dir); + } + + } catch (IOException e) { + e.printStackTrace(); + } + + } + + private Set getDirectories(byte[] zipData) throws IOException { + Set dirs = new HashSet<>(); + ZipInputStream stream = new ZipInputStream(new ByteArrayInputStream(zipData)); + + // exclude main directory + ZipEntry entry = stream.getNextEntry(); + int mainDirLength = entry.getName().length(); + + while ((entry = stream.getNextEntry()) != null) { + if (entry.isDirectory()) { + String dir = entry.getName(); + // remove / at the end + dirs.add(dir.substring(mainDirLength, dir.length() - 1)); + } + } + return dirs; + } + + private void readWavFilesDirectory(byte[] zipData, String dir) throws IOException { + ZipInputStream stream = new ZipInputStream(new ByteArrayInputStream(zipData)); + ZipEntry entry; + while ((entry = stream.getNextEntry()) != null) { + if (entry.getName().startsWith(dir) && entry.isDirectory()) { + readWavFilesDirectory(stream, dir); + // dont read next dir + break; + } + } + } + + private void readWavFilesDirectory(ZipInputStream stream, String dir) throws IOException { + ZipEntry entry; + while ((entry = stream.getNextEntry()) != null && !entry.isDirectory() && entry.getName().endsWith(".wav")) { + readWavFile(entry, dir); + } + } + + private void readWavFile(ZipEntry entry, String dir) { + InputStream stream = new ByteArrayInputStream(entry.getExtra()); + double[] data = ReaderWavFile.readMonoFromWavFile(stream); + samples.add(data); + labels.add(dir); + } + + private byte[] getZipData(InputStream in) throws IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + byte[] dataBuffer = new byte[1024]; + int bytesRead; + while ((bytesRead = in.read(dataBuffer, 0, 1024)) != -1) { + out.write(dataBuffer, 0, bytesRead); + } + return out.toByteArray(); + } + + private byte[] getZipData(URL url) throws IOException { + InputStream in = new BufferedInputStream(url.openStream()); + return getZipData(in); + } } From 53be83e0a1893fffb82cc82abf5738f1cc584c8a Mon Sep 17 00:00:00 2001 From: Jessica Priebe Date: Sat, 20 Jan 2024 13:38:00 +0100 Subject: [PATCH 098/133] styling --- .../sysds/runtime/io/ReaderWavFile.java | 36 +++++++++-- .../matrix/data/LibMatrixKeywordSpotting.java | 63 +++++++++++++++---- 2 files changed, 83 insertions(+), 16 deletions(-) diff --git a/src/main/java/org/apache/sysds/runtime/io/ReaderWavFile.java b/src/main/java/org/apache/sysds/runtime/io/ReaderWavFile.java index d4295ff10dc..95966d6bff5 100644 --- a/src/main/java/org/apache/sysds/runtime/io/ReaderWavFile.java +++ b/src/main/java/org/apache/sysds/runtime/io/ReaderWavFile.java @@ -1,17 +1,41 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + package org.apache.sysds.runtime.io; -import javax.sound.sampled.*; import java.io.File; import java.io.IOException; import java.io.InputStream; +import javax.sound.sampled.AudioSystem; +import javax.sound.sampled.AudioFormat; +import javax.sound.sampled.AudioInputStream; +import javax.sound.sampled.UnsupportedAudioFileException; + public class ReaderWavFile { - public static double[] readMonoFromWavFile(String filePath) { + public static double[] readMonoAudioFromWavFile(String filePath) { + try { // open audio file AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(new File(filePath)); - double[] audioValues = readMonoFromWavFile(audioInputStream); + double[] audioValues = readMonoAudioFromWavFile(audioInputStream); audioInputStream.close(); return audioValues; @@ -19,9 +43,10 @@ public static double[] readMonoFromWavFile(String filePath) { e.printStackTrace(); return null; } + } - public static double[] readMonoFromWavFile(InputStream inputStream) { + public static double[] readMonoAudioFromWavFile(InputStream inputStream) { try { // open audio file @@ -65,12 +90,13 @@ public static double[] readMonoFromWavFile(InputStream inputStream) { // close audio streams monoAudioInputStream.close(); audioInputStream.close(); - return audioValues; } catch (UnsupportedAudioFileException | IOException e) { e.printStackTrace(); return null; } + } + } diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java index 0702d771a9e..33d8adef302 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java @@ -1,10 +1,39 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + package org.apache.sysds.runtime.matrix.data; import org.apache.sysds.runtime.io.ReaderWavFile; -import java.io.*; import java.net.URL; -import java.util.*; + +import java.io.InputStream; +import java.io.FileInputStream; +import java.io.BufferedInputStream; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; + +import java.util.List; +import java.util.ArrayList; +import java.util.Set; +import java.util.HashSet; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; @@ -13,8 +42,6 @@ public class LibMatrixKeywordSpotting { List samples = new ArrayList<>(); List labels = new ArrayList<>(); - - public LibMatrixKeywordSpotting() { // load all data @@ -33,15 +60,15 @@ public LibMatrixKeywordSpotting() { // TODO: // train model - // use gaussianClassifier/CNN??? - + // use gaussianClassifier??? // [prior, means, covs, det] = gaussianClassifier(D=X, C=y, varSmoothing=$2); // use global variables for classifier } - private double[] convertWaveToMagnitudesSpectrogram(double[] wave){ + // length=255, overlap=128 + // TODO: adjust stft double[][] spectrogram = LibMatrixSTFT.one_dim_stft(wave, 255, 128); int cols = spectrogram[0].length; @@ -49,18 +76,19 @@ private double[] convertWaveToMagnitudesSpectrogram(double[] wave){ for (int i = 0; i < cols; i++){ magnitudes[i] = Math.sqrt(Math.pow(spectrogram[0][i], 2) + Math.pow(spectrogram[0][i], 2)); } + return magnitudes; } - public String getCommandForFile(String filePath){ + public String predictCommandForFile(String filePath){ // read wave file - double[] wave = ReaderWavFile.readMonoFromWavFile(filePath); + double[] wave = ReaderWavFile.readMonoAudioFromWavFile(filePath); // convert waveforms to spectrogram double[] magnitudes = convertWaveToMagnitudesSpectrogram(wave); - // use global variables for classifier + // use global variables for prediction // TODO return null; @@ -92,6 +120,7 @@ private void loadAllData(){ } private Set getDirectories(byte[] zipData) throws IOException { + Set dirs = new HashSet<>(); ZipInputStream stream = new ZipInputStream(new ByteArrayInputStream(zipData)); @@ -106,12 +135,15 @@ private Set getDirectories(byte[] zipData) throws IOException { dirs.add(dir.substring(mainDirLength, dir.length() - 1)); } } + return dirs; } private void readWavFilesDirectory(byte[] zipData, String dir) throws IOException { + ZipInputStream stream = new ZipInputStream(new ByteArrayInputStream(zipData)); ZipEntry entry; + while ((entry = stream.getNextEntry()) != null) { if (entry.getName().startsWith(dir) && entry.isDirectory()) { readWavFilesDirectory(stream, dir); @@ -119,29 +151,37 @@ private void readWavFilesDirectory(byte[] zipData, String dir) throws IOExceptio break; } } + } private void readWavFilesDirectory(ZipInputStream stream, String dir) throws IOException { + ZipEntry entry; while ((entry = stream.getNextEntry()) != null && !entry.isDirectory() && entry.getName().endsWith(".wav")) { readWavFile(entry, dir); } + } private void readWavFile(ZipEntry entry, String dir) { + InputStream stream = new ByteArrayInputStream(entry.getExtra()); - double[] data = ReaderWavFile.readMonoFromWavFile(stream); + double[] data = ReaderWavFile.readMonoAudioFromWavFile(stream); samples.add(data); labels.add(dir); + } private byte[] getZipData(InputStream in) throws IOException { + ByteArrayOutputStream out = new ByteArrayOutputStream(); byte[] dataBuffer = new byte[1024]; + int bytesRead; while ((bytesRead = in.read(dataBuffer, 0, 1024)) != -1) { out.write(dataBuffer, 0, bytesRead); } + return out.toByteArray(); } @@ -149,4 +189,5 @@ private byte[] getZipData(URL url) throws IOException { InputStream in = new BufferedInputStream(url.openStream()); return getZipData(in); } + } From c9f0126fa0f49dd8382a1130c79bfecbb1337a90 Mon Sep 17 00:00:00 2001 From: Jessica Priebe Date: Sat, 20 Jan 2024 15:47:09 +0100 Subject: [PATCH 099/133] added zip temporarily --- .../matrix/data/LibMatrixKeywordSpotting.java | 2 +- .../data/mini_speech_commands_slimmed.zip | Bin 0 -> 250932 bytes 2 files changed, 1 insertion(+), 1 deletion(-) create mode 100644 src/main/java/org/apache/sysds/runtime/matrix/data/mini_speech_commands_slimmed.zip diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java index 33d8adef302..5be712bb91e 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java @@ -100,7 +100,7 @@ private void loadAllData(){ // String url = "http://storage.googleapis.com/download.tensorflow.org/data/mini_speech_commands.zip"; // Set dirs = Set.of("yes", "no"); - String zipFilePath = "/Users/jessica/desktop/mini_speech_commands.zip"; + String zipFilePath = "./src/main/java/org/apache/sysds/runtime/matrix/data/mini_speech_commands_slimmed.zip"; try { // get zip data diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/mini_speech_commands_slimmed.zip b/src/main/java/org/apache/sysds/runtime/matrix/data/mini_speech_commands_slimmed.zip new file mode 100644 index 0000000000000000000000000000000000000000..bd0cee4c1fe81a157f57a543ff9cd7d57beceeca GIT binary patch literal 250932 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%0AQ0=*+L1VZ@PLL>zsEkfl(+;n%rEGlt5{ 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

    g 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 literal 0 HcmV?d00001 From 124b964902aa1c1a863f1f520b517d997b660f00 Mon Sep 17 00:00:00 2001 From: Jessica Priebe Date: Wed, 14 Feb 2024 20:07:27 +0100 Subject: [PATCH 100/133] use url for zip --- .../matrix/data/LibMatrixKeywordSpotting.java | 18 +++++------------- 1 file changed, 5 insertions(+), 13 deletions(-) diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java index 5be712bb91e..d7a33361857 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java @@ -96,15 +96,11 @@ public String predictCommandForFile(String filePath){ private void loadAllData(){ - // doesn't work for url - // String url = "http://storage.googleapis.com/download.tensorflow.org/data/mini_speech_commands.zip"; - // Set dirs = Set.of("yes", "no"); - - String zipFilePath = "./src/main/java/org/apache/sysds/runtime/matrix/data/mini_speech_commands_slimmed.zip"; + String url = "http://storage.googleapis.com/download.tensorflow.org/data/mini_speech_commands.zip"; try { // get zip data - byte[] zipData = getZipData(new FileInputStream(zipFilePath)); + byte[] zipData = getZipData(new URL(url)); // get folder names Set dirs = getDirectories(zipData); @@ -131,7 +127,7 @@ private Set getDirectories(byte[] zipData) throws IOException { while ((entry = stream.getNextEntry()) != null) { if (entry.isDirectory()) { String dir = entry.getName(); - // remove / at the end + // remove "/" at the end dirs.add(dir.substring(mainDirLength, dir.length() - 1)); } } @@ -172,7 +168,8 @@ private void readWavFile(ZipEntry entry, String dir) { } - private byte[] getZipData(InputStream in) throws IOException { + private byte[] getZipData(URL url) throws IOException { + InputStream in = url.openConnection().getInputStream(); ByteArrayOutputStream out = new ByteArrayOutputStream(); byte[] dataBuffer = new byte[1024]; @@ -185,9 +182,4 @@ private byte[] getZipData(InputStream in) throws IOException { return out.toByteArray(); } - private byte[] getZipData(URL url) throws IOException { - InputStream in = new BufferedInputStream(url.openStream()); - return getZipData(in); - } - } From 5136a82b132618b4f3843f8dd76a1e753307c445 Mon Sep 17 00:00:00 2001 From: Jessica Priebe Date: Wed, 14 Feb 2024 22:01:32 +0100 Subject: [PATCH 101/133] improved reading zip and wave files --- .../sysds/runtime/io/ReaderWavFile.java | 2 + .../matrix/data/LibMatrixKeywordSpotting.java | 141 +++++++----------- 2 files changed, 58 insertions(+), 85 deletions(-) diff --git a/src/main/java/org/apache/sysds/runtime/io/ReaderWavFile.java b/src/main/java/org/apache/sysds/runtime/io/ReaderWavFile.java index 95966d6bff5..ad795628f6f 100644 --- a/src/main/java/org/apache/sysds/runtime/io/ReaderWavFile.java +++ b/src/main/java/org/apache/sysds/runtime/io/ReaderWavFile.java @@ -30,6 +30,7 @@ public class ReaderWavFile { + /* public static double[] readMonoAudioFromWavFile(String filePath) { try { @@ -45,6 +46,7 @@ public static double[] readMonoAudioFromWavFile(String filePath) { } } + */ public static double[] readMonoAudioFromWavFile(InputStream inputStream) { diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java index d7a33361857..ccbde614549 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java @@ -39,93 +39,74 @@ public class LibMatrixKeywordSpotting { - List samples = new ArrayList<>(); - List labels = new ArrayList<>(); - public LibMatrixKeywordSpotting() { // load all data - // data: http://storage.googleapis.com/download.tensorflow.org/data/mini_speech_commands.zip + String url = "http://storage.googleapis.com/download.tensorflow.org/data/mini_speech_commands.zip"; + // zip contains command folders which contain corresponding .wav files - // maybe change label to int? - loadAllData(); + List waves = new ArrayList<>(); + List labels = new ArrayList<>(); + loadAllData(url, waves, labels); // convert waveforms to magnitudes of spectrogram // uses stft - for (int i = 0; i < samples.size(); i++){ - double[] wave = samples.get(i); + List spectrograms = new ArrayList<>(); + for(double[] wave : waves) { double[] magnitudes = convertWaveToMagnitudesSpectrogram(wave); - samples.set(i, magnitudes); + spectrograms.add(magnitudes); } // TODO: - // train model - // use gaussianClassifier??? - // [prior, means, covs, det] = gaussianClassifier(D=X, C=y, varSmoothing=$2); - // use global variables for classifier + // csv for spectograms + // csv for labels } - private double[] convertWaveToMagnitudesSpectrogram(double[] wave){ - - // length=255, overlap=128 - // TODO: adjust stft - double[][] spectrogram = LibMatrixSTFT.one_dim_stft(wave, 255, 128); - - int cols = spectrogram[0].length; - double[] magnitudes = new double[cols]; - for (int i = 0; i < cols; i++){ - magnitudes[i] = Math.sqrt(Math.pow(spectrogram[0][i], 2) + Math.pow(spectrogram[0][i], 2)); - } + private void loadAllData(String url, List waves, List labels) { - return magnitudes; - } - - public String predictCommandForFile(String filePath){ + try { + // get zip data + byte[] zipData = getBytesZipFile(new URL(url)); - // read wave file - double[] wave = ReaderWavFile.readMonoAudioFromWavFile(filePath); + // get folder names + List dirs = getDirectories(zipData); - // convert waveforms to spectrogram - double[] magnitudes = convertWaveToMagnitudesSpectrogram(wave); + readWaveFiles(zipData, dirs, waves, labels); - // use global variables for prediction - // TODO + } + catch(IOException e) { + e.printStackTrace(); + } - return null; } - private void loadAllData(){ - - String url = "http://storage.googleapis.com/download.tensorflow.org/data/mini_speech_commands.zip"; + private byte[] getBytesZipFile(URL url) throws IOException { - try { - // get zip data - byte[] zipData = getZipData(new URL(url)); - - // get folder names - Set dirs = getDirectories(zipData); + InputStream in = url.openConnection().getInputStream(); - for (String dir : dirs) { - readWavFilesDirectory(zipData, dir); - } + ByteArrayOutputStream out = new ByteArrayOutputStream(); + byte[] dataBuffer = new byte[1024]; - } catch (IOException e) { - e.printStackTrace(); + int bytesRead; + while((bytesRead = in.read(dataBuffer, 0, 1024)) != -1) { + out.write(dataBuffer, 0, bytesRead); } + return out.toByteArray(); + } - private Set getDirectories(byte[] zipData) throws IOException { + private List getDirectories(byte[] zipData) throws IOException { - Set dirs = new HashSet<>(); + List dirs = new ArrayList<>(); ZipInputStream stream = new ZipInputStream(new ByteArrayInputStream(zipData)); // exclude main directory ZipEntry entry = stream.getNextEntry(); int mainDirLength = entry.getName().length(); - while ((entry = stream.getNextEntry()) != null) { - if (entry.isDirectory()) { + while((entry = stream.getNextEntry()) != null) { + if(entry.isDirectory()) { String dir = entry.getName(); // remove "/" at the end dirs.add(dir.substring(mainDirLength, dir.length() - 1)); @@ -135,51 +116,41 @@ private Set getDirectories(byte[] zipData) throws IOException { return dirs; } - private void readWavFilesDirectory(byte[] zipData, String dir) throws IOException { + private void readWaveFiles(byte[] zipData, List dirs, List waves, List labels) + throws IOException { ZipInputStream stream = new ZipInputStream(new ByteArrayInputStream(zipData)); ZipEntry entry; + String dir = dirs.get(0); - while ((entry = stream.getNextEntry()) != null) { - if (entry.getName().startsWith(dir) && entry.isDirectory()) { - readWavFilesDirectory(stream, dir); - // dont read next dir - break; - } - } + while((entry = stream.getNextEntry()) != null) { + if(!entry.isDirectory() && entry.getName().startsWith(dir) && entry.getName().endsWith(".wav")) { - } - - private void readWavFilesDirectory(ZipInputStream stream, String dir) throws IOException { - - ZipEntry entry; - while ((entry = stream.getNextEntry()) != null && !entry.isDirectory() && entry.getName().endsWith(".wav")) { - readWavFile(entry, dir); + // read file + double[] data = ReaderWavFile.readMonoAudioFromWavFile(new ByteArrayInputStream(entry.getExtra())); + waves.add(data); + labels.add(dir); + } + else { + dir = dirs.get(dirs.indexOf(dir) + 1); + } } } - private void readWavFile(ZipEntry entry, String dir) { - - InputStream stream = new ByteArrayInputStream(entry.getExtra()); - double[] data = ReaderWavFile.readMonoAudioFromWavFile(stream); - samples.add(data); - labels.add(dir); - - } - - private byte[] getZipData(URL url) throws IOException { - InputStream in = url.openConnection().getInputStream(); + private double[] convertWaveToMagnitudesSpectrogram(double[] wave) { - ByteArrayOutputStream out = new ByteArrayOutputStream(); - byte[] dataBuffer = new byte[1024]; + // length=255, overlap=128 + // TODO: adjust stft + double[][] spectrogram = LibMatrixSTFT.one_dim_stft(wave, 255, 128); - int bytesRead; - while ((bytesRead = in.read(dataBuffer, 0, 1024)) != -1) { - out.write(dataBuffer, 0, bytesRead); + int cols = spectrogram[0].length; + double[] magnitudes = new double[cols]; + for(int i = 0; i < cols; i++) { + magnitudes[i] = Math.sqrt(Math.pow(spectrogram[0][i], 2) + Math.pow(spectrogram[0][i], 2)); } - return out.toByteArray(); + return magnitudes; } } From 209a4b1e8cad799ec815921c8b26d9248b2a7c66 Mon Sep 17 00:00:00 2001 From: Jessica Priebe Date: Thu, 15 Feb 2024 00:49:17 +0100 Subject: [PATCH 102/133] executable LibMatrixKeywordSpotting --- .../sysds/runtime/io/ReaderWavFile.java | 21 +++++++----------- .../matrix/data/LibMatrixKeywordSpotting.java | 22 +++++++++---------- 2 files changed, 18 insertions(+), 25 deletions(-) diff --git a/src/main/java/org/apache/sysds/runtime/io/ReaderWavFile.java b/src/main/java/org/apache/sysds/runtime/io/ReaderWavFile.java index ad795628f6f..7904abea83c 100644 --- a/src/main/java/org/apache/sysds/runtime/io/ReaderWavFile.java +++ b/src/main/java/org/apache/sysds/runtime/io/ReaderWavFile.java @@ -30,7 +30,6 @@ public class ReaderWavFile { - /* public static double[] readMonoAudioFromWavFile(String filePath) { try { @@ -40,13 +39,13 @@ public static double[] readMonoAudioFromWavFile(String filePath) { audioInputStream.close(); return audioValues; - } catch (UnsupportedAudioFileException | IOException e) { + } + catch(UnsupportedAudioFileException | IOException e) { e.printStackTrace(); return null; } } - */ public static double[] readMonoAudioFromWavFile(InputStream inputStream) { @@ -56,13 +55,8 @@ public static double[] readMonoAudioFromWavFile(InputStream inputStream) { // collapse channels to mono channel int channels = 1; - AudioFormat monoAudioFormat = new AudioFormat( - audioInputStream.getFormat().getSampleRate(), - audioInputStream.getFormat().getSampleSizeInBits(), - channels, - true, - false - ); + AudioFormat monoAudioFormat = new AudioFormat(audioInputStream.getFormat().getSampleRate(), + audioInputStream.getFormat().getSampleSizeInBits(), channels, true, false); AudioInputStream monoAudioInputStream = AudioSystem.getAudioInputStream(monoAudioFormat, audioInputStream); // curation of audio @@ -75,13 +69,13 @@ public static double[] readMonoAudioFromWavFile(InputStream inputStream) { int bytesRead = audioInputStream.read(audioData); // read operation failed - if (bytesRead == -1) { + if(bytesRead == -1) { return null; } // convert byte array to double array double[] audioValues = new double[numFrames]; - for (int i = 0, frameIndex = 0; i < bytesRead; i += frameSize, frameIndex++) { + for(int i = 0, frameIndex = 0; i < bytesRead; i += frameSize, frameIndex++) { // 16-bit PCM encoding // combine two bytes into a 16-bit integer (short) short sampleValue = (short) ((audioData[i + 1] << 8) | (audioData[i] & 0xFF)); @@ -94,7 +88,8 @@ public static double[] readMonoAudioFromWavFile(InputStream inputStream) { audioInputStream.close(); return audioValues; - } catch (UnsupportedAudioFileException | IOException e) { + } + catch(UnsupportedAudioFileException | IOException e) { e.printStackTrace(); return null; } diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java index ccbde614549..f7c1035d870 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java @@ -24,22 +24,18 @@ import java.net.URL; import java.io.InputStream; -import java.io.FileInputStream; -import java.io.BufferedInputStream; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; import java.util.List; import java.util.ArrayList; -import java.util.Set; -import java.util.HashSet; import java.util.zip.ZipEntry; import java.util.zip.ZipInputStream; public class LibMatrixKeywordSpotting { - public LibMatrixKeywordSpotting() { + public static void main(String[] args) { // load all data String url = "http://storage.googleapis.com/download.tensorflow.org/data/mini_speech_commands.zip"; @@ -58,11 +54,13 @@ public LibMatrixKeywordSpotting() { } // TODO: - // csv for spectograms - // csv for labels + // csv for spectrograms + // csv for labels - use index? + // if we use index we also need a csv to translate index back to command + } - private void loadAllData(String url, List waves, List labels) { + private static void loadAllData(String url, List waves, List labels) { try { // get zip data @@ -80,7 +78,7 @@ private void loadAllData(String url, List waves, List labels) } - private byte[] getBytesZipFile(URL url) throws IOException { + private static byte[] getBytesZipFile(URL url) throws IOException { InputStream in = url.openConnection().getInputStream(); @@ -96,7 +94,7 @@ private byte[] getBytesZipFile(URL url) throws IOException { } - private List getDirectories(byte[] zipData) throws IOException { + private static List getDirectories(byte[] zipData) throws IOException { List dirs = new ArrayList<>(); ZipInputStream stream = new ZipInputStream(new ByteArrayInputStream(zipData)); @@ -116,7 +114,7 @@ private List getDirectories(byte[] zipData) throws IOException { return dirs; } - private void readWaveFiles(byte[] zipData, List dirs, List waves, List labels) + private static void readWaveFiles(byte[] zipData, List dirs, List waves, List labels) throws IOException { ZipInputStream stream = new ZipInputStream(new ByteArrayInputStream(zipData)); @@ -138,7 +136,7 @@ private void readWaveFiles(byte[] zipData, List dirs, List wav } - private double[] convertWaveToMagnitudesSpectrogram(double[] wave) { + private static double[] convertWaveToMagnitudesSpectrogram(double[] wave) { // length=255, overlap=128 // TODO: adjust stft From 92ac788ffb8565f058898566bb53ffac3f87f8cc Mon Sep 17 00:00:00 2001 From: Jessica Priebe Date: Thu, 15 Feb 2024 02:02:24 +0100 Subject: [PATCH 103/133] fixed readWaveFiles --- .../matrix/data/LibMatrixKeywordSpotting.java | 41 ++++++++----------- 1 file changed, 16 insertions(+), 25 deletions(-) diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java index f7c1035d870..a0bfc039983 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java @@ -27,6 +27,7 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.io.FileInputStream; import java.util.List; import java.util.ArrayList; @@ -45,18 +46,11 @@ public static void main(String[] args) { List labels = new ArrayList<>(); loadAllData(url, waves, labels); - // convert waveforms to magnitudes of spectrogram - // uses stft - List spectrograms = new ArrayList<>(); - for(double[] wave : waves) { - double[] magnitudes = convertWaveToMagnitudesSpectrogram(wave); - spectrograms.add(magnitudes); - } - // TODO: - // csv for spectrograms + // csv for waves // csv for labels - use index? // if we use index we also need a csv to translate index back to command + // don't forget magnitudes after stft! } @@ -80,7 +74,9 @@ private static void loadAllData(String url, List waves, List l private static byte[] getBytesZipFile(URL url) throws IOException { - InputStream in = url.openConnection().getInputStream(); + //InputStream in = url.openConnection().getInputStream(); + String zipFilePath = "./src/main/java/org/apache/sysds/runtime/matrix/data/mini_speech_commands_slimmed.zip"; + InputStream in = new FileInputStream(zipFilePath); ByteArrayOutputStream out = new ByteArrayOutputStream(); byte[] dataBuffer = new byte[1024]; @@ -122,33 +118,28 @@ private static void readWaveFiles(byte[] zipData, List dirs, List dirs){ - int cols = spectrogram[0].length; - double[] magnitudes = new double[cols]; - for(int i = 0; i < cols; i++) { - magnitudes[i] = Math.sqrt(Math.pow(spectrogram[0][i], 2) + Math.pow(spectrogram[0][i], 2)); + for (String dir : dirs){ + if(entry.getName().startsWith(dir)){ + return dir; + } } - return magnitudes; + return null; } } From cb52d504620c1386d940eda4b0a3ae38873f795e Mon Sep 17 00:00:00 2001 From: Jessica Priebe Date: Fri, 16 Feb 2024 14:10:37 +0100 Subject: [PATCH 104/133] downloader data for keywordspotting --- .../sysds/runtime/io/DownloaderZip.java | 45 ++++++++ .../matrix/data/LibMatrixKeywordSpotting.java | 106 ++++++------------ 2 files changed, 77 insertions(+), 74 deletions(-) create mode 100644 src/main/java/org/apache/sysds/runtime/io/DownloaderZip.java diff --git a/src/main/java/org/apache/sysds/runtime/io/DownloaderZip.java b/src/main/java/org/apache/sysds/runtime/io/DownloaderZip.java new file mode 100644 index 00000000000..c398b163d62 --- /dev/null +++ b/src/main/java/org/apache/sysds/runtime/io/DownloaderZip.java @@ -0,0 +1,45 @@ +package org.apache.sysds.runtime.io; + +import java.io.*; + +import java.net.URL; +import java.util.zip.*; + +public class DownloaderZip { + + public static void downloaderZip(String url, File dest, String startsWith, String endsWith) { + + try { + ZipInputStream in = new ZipInputStream(new BufferedInputStream(new URL(url).openConnection().getInputStream())); + + + ZipEntry entry; + while((entry = in.getNextEntry()) != null) { + StringBuilder path = new StringBuilder(dest.getPath()).append('/').append(entry.getName()); + File file = new File(path.toString()); + + if(entry.isDirectory()){ + file.mkdirs(); + continue; + } + if(entry.getName().startsWith(startsWith) && entry.getName().endsWith(endsWith)){ + FileOutputStream out = new FileOutputStream(file); + for (int read = in.read(); read != -1; read = in.read()){ + out.write(read); + } + out.close(); + + // double[] z = ReaderWavFile.readMonoAudioFromWavFile(file.getPath()); + // System.out.println("hi"); + } + + } + + } catch ( IOException e){ + e.printStackTrace(); + } + + + } + +} diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java index a0bfc039983..aa62d31d65d 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java @@ -19,32 +19,33 @@ package org.apache.sysds.runtime.matrix.data; -import org.apache.sysds.runtime.io.ReaderWavFile; +import org.apache.sysds.runtime.io.*; -import java.net.URL; - -import java.io.InputStream; -import java.io.ByteArrayInputStream; -import java.io.ByteArrayOutputStream; +import java.io.File; import java.io.IOException; -import java.io.FileInputStream; + import java.util.List; import java.util.ArrayList; -import java.util.zip.ZipEntry; -import java.util.zip.ZipInputStream; public class LibMatrixKeywordSpotting { public static void main(String[] args) { - // load all data + // download data String url = "http://storage.googleapis.com/download.tensorflow.org/data/mini_speech_commands.zip"; + File dest = new File("./tmp"); + String startsWith = "mini_speech_commands"; + String endsWith = ".wav"; + DownloaderZip.downloaderZip(url, dest, startsWith, endsWith); // zip contains command folders which contain corresponding .wav files List waves = new ArrayList<>(); - List labels = new ArrayList<>(); - loadAllData(url, waves, labels); + List labels = new ArrayList<>(); + List commands = new ArrayList<>(); + + String sourceDir = "./tmp/mini_speech_commands"; + extractData(sourceDir, waves, labels, commands); // TODO: // csv for waves @@ -54,16 +55,16 @@ public static void main(String[] args) { } - private static void loadAllData(String url, List waves, List labels) { + private static void extractData(String sourceDir, List waves, List labels, List commands) { try { - // get zip data - byte[] zipData = getBytesZipFile(new URL(url)); - // get folder names - List dirs = getDirectories(zipData); + // get directory names + getDirectories(sourceDir, commands); - readWaveFiles(zipData, dirs, waves, labels); + for(String command : commands){ + readWaveFiles(sourceDir, command, waves, labels, commands); + } } catch(IOException e) { @@ -72,74 +73,31 @@ private static void loadAllData(String url, List waves, List l } - private static byte[] getBytesZipFile(URL url) throws IOException { - - //InputStream in = url.openConnection().getInputStream(); - String zipFilePath = "./src/main/java/org/apache/sysds/runtime/matrix/data/mini_speech_commands_slimmed.zip"; - InputStream in = new FileInputStream(zipFilePath); - ByteArrayOutputStream out = new ByteArrayOutputStream(); - byte[] dataBuffer = new byte[1024]; + private static void getDirectories(String sourceDir, List commands) throws IOException { - int bytesRead; - while((bytesRead = in.read(dataBuffer, 0, 1024)) != -1) { - out.write(dataBuffer, 0, bytesRead); - } - - return out.toByteArray(); - - } + File[] subDirs = new File(sourceDir).listFiles(); + if(subDirs == null) return; - private static List getDirectories(byte[] zipData) throws IOException { - - List dirs = new ArrayList<>(); - ZipInputStream stream = new ZipInputStream(new ByteArrayInputStream(zipData)); - - // exclude main directory - ZipEntry entry = stream.getNextEntry(); - int mainDirLength = entry.getName().length(); - - while((entry = stream.getNextEntry()) != null) { - if(entry.isDirectory()) { - String dir = entry.getName(); - // remove "/" at the end - dirs.add(dir.substring(mainDirLength, dir.length() - 1)); + for(File c: subDirs){ + if(c.isDirectory()){ + commands.add(c.getName()); } } - return dirs; } - private static void readWaveFiles(byte[] zipData, List dirs, List waves, List labels) - throws IOException { - - ZipInputStream stream = new ZipInputStream(new ByteArrayInputStream(zipData)); - ZipEntry entry; - String dir = dirs.get(0); - - while((entry = stream.getNextEntry()) != null) { - if(entry.getName().endsWith(".wav")) { - if(!entry.getName().contains(dir)){ - dir = findDir(entry, dirs); - } - // read file - double[] data = ReaderWavFile.readMonoAudioFromWavFile(new ByteArrayInputStream(entry.getExtra())); - waves.add(data); - labels.add(dir); - } - } - - } + private static void readWaveFiles(String sourceDir, String command, List waves, List labels, List commands){ - private static String findDir(ZipEntry entry, List dirs){ + String path = sourceDir + '/' + command; + File dir = new File(path); - for (String dir : dirs){ - if(entry.getName().startsWith(dir)){ - return dir; - } + File[] waveFiles = dir.listFiles(); + for(File file: waveFiles){ + waves.add(ReaderWavFile.readMonoAudioFromWavFile(file.getPath())); + labels.add(commands.indexOf(command)); } - return null; } } From 79a7506c630fcae63f8d3df79c2687976b5ac496 Mon Sep 17 00:00:00 2001 From: Jessica Priebe Date: Fri, 16 Feb 2024 20:41:58 +0100 Subject: [PATCH 105/133] tried converting bytearrayinput to audioinputarray --- .../sysds/runtime/io/DownloaderZip.java | 21 +++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/apache/sysds/runtime/io/DownloaderZip.java b/src/main/java/org/apache/sysds/runtime/io/DownloaderZip.java index c398b163d62..409a49dfcd1 100644 --- a/src/main/java/org/apache/sysds/runtime/io/DownloaderZip.java +++ b/src/main/java/org/apache/sysds/runtime/io/DownloaderZip.java @@ -1,5 +1,6 @@ package org.apache.sysds.runtime.io; +import javax.sound.sampled.*; import java.io.*; import java.net.URL; @@ -14,6 +15,7 @@ public static void downloaderZip(String url, File dest, String startsWith, Strin ZipEntry entry; + int cnt = 0; while((entry = in.getNextEntry()) != null) { StringBuilder path = new StringBuilder(dest.getPath()).append('/').append(entry.getName()); File file = new File(path.toString()); @@ -23,14 +25,29 @@ public static void downloaderZip(String url, File dest, String startsWith, Strin continue; } if(entry.getName().startsWith(startsWith) && entry.getName().endsWith(endsWith)){ + + /* + AudioFormat format = new AudioFormat( AudioFormat.Encoding.PCM_SIGNED, 16000, 16, 1, 2, 16000, false); + int length = (int) Math.ceil((double) entry.getExtra().length / format.getFrameSize()); + AudioInputStream audio = new AudioInputStream(new ByteArrayInputStream(entry.getExtra()), format, length); + AudioSystem.write(audio, AudioFileFormat.Type.WAVE, file); + */ + FileOutputStream out = new FileOutputStream(file); for (int read = in.read(); read != -1; read = in.read()){ out.write(read); } out.close(); - // double[] z = ReaderWavFile.readMonoAudioFromWavFile(file.getPath()); - // System.out.println("hi"); + cnt++; + if(cnt%50 == 0){ + System.out.println(cnt + "/8008"); + } + + // TODO: only for debugging + if(cnt == 200){ + break; + } } } From 882be22eeb4cb78dcff351719ef8ba520019bde3 Mon Sep 17 00:00:00 2001 From: Jessica Priebe Date: Fri, 16 Feb 2024 21:03:36 +0100 Subject: [PATCH 106/133] fixed --- .../org/apache/sysds/runtime/io/ReaderWavFile.java | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/apache/sysds/runtime/io/ReaderWavFile.java b/src/main/java/org/apache/sysds/runtime/io/ReaderWavFile.java index 7904abea83c..03beffe0101 100644 --- a/src/main/java/org/apache/sysds/runtime/io/ReaderWavFile.java +++ b/src/main/java/org/apache/sysds/runtime/io/ReaderWavFile.java @@ -21,7 +21,6 @@ import java.io.File; import java.io.IOException; -import java.io.InputStream; import javax.sound.sampled.AudioSystem; import javax.sound.sampled.AudioFormat; @@ -47,11 +46,9 @@ public static double[] readMonoAudioFromWavFile(String filePath) { } - public static double[] readMonoAudioFromWavFile(InputStream inputStream) { + public static double[] readMonoAudioFromWavFile(AudioInputStream audioInputStream) { try { - // open audio file - AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(inputStream); // collapse channels to mono channel int channels = 1; @@ -83,13 +80,12 @@ public static double[] readMonoAudioFromWavFile(InputStream inputStream) { audioValues[frameIndex] = sampleValue / 32768.0; } - // close audio streams + // close mono audio stream monoAudioInputStream.close(); - audioInputStream.close(); return audioValues; } - catch(UnsupportedAudioFileException | IOException e) { + catch(IOException e) { e.printStackTrace(); return null; } From fde0258c3b298e9b407e8b53795f186018f458e4 Mon Sep 17 00:00:00 2001 From: Jessica Priebe Date: Fri, 16 Feb 2024 21:02:16 +0100 Subject: [PATCH 107/133] fixed --- .../sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java index aa62d31d65d..fa04ff9871f 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java @@ -37,7 +37,7 @@ public static void main(String[] args) { File dest = new File("./tmp"); String startsWith = "mini_speech_commands"; String endsWith = ".wav"; - DownloaderZip.downloaderZip(url, dest, startsWith, endsWith); + // DownloaderZip.downloaderZip(url, dest, startsWith, endsWith); // zip contains command folders which contain corresponding .wav files List waves = new ArrayList<>(); From 73cae3274ab230f449ccb8beff21406e13e17922 Mon Sep 17 00:00:00 2001 From: Jessica Priebe Date: Fri, 16 Feb 2024 21:04:11 +0100 Subject: [PATCH 108/133] fixed --- .../sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java index fa04ff9871f..aa62d31d65d 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java @@ -37,7 +37,7 @@ public static void main(String[] args) { File dest = new File("./tmp"); String startsWith = "mini_speech_commands"; String endsWith = ".wav"; - // DownloaderZip.downloaderZip(url, dest, startsWith, endsWith); + DownloaderZip.downloaderZip(url, dest, startsWith, endsWith); // zip contains command folders which contain corresponding .wav files List waves = new ArrayList<>(); From b91d5e40bc7f2a1c8fe19142cbed6b326c4d02e6 Mon Sep 17 00:00:00 2001 From: Jessica Priebe Date: Fri, 16 Feb 2024 21:38:30 +0100 Subject: [PATCH 109/133] delete dir after extracting the data + formatting --- .../sysds/runtime/io/DownloaderZip.java | 56 +++++++++++++------ .../matrix/data/LibMatrixKeywordSpotting.java | 32 ++++++----- 2 files changed, 58 insertions(+), 30 deletions(-) diff --git a/src/main/java/org/apache/sysds/runtime/io/DownloaderZip.java b/src/main/java/org/apache/sysds/runtime/io/DownloaderZip.java index 409a49dfcd1..e9765272fd5 100644 --- a/src/main/java/org/apache/sysds/runtime/io/DownloaderZip.java +++ b/src/main/java/org/apache/sysds/runtime/io/DownloaderZip.java @@ -1,18 +1,40 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ + package org.apache.sysds.runtime.io; -import javax.sound.sampled.*; -import java.io.*; +import java.io.File; +import java.io.IOException; +import java.io.BufferedInputStream; +import java.io.FileOutputStream; import java.net.URL; -import java.util.zip.*; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; public class DownloaderZip { public static void downloaderZip(String url, File dest, String startsWith, String endsWith) { try { - ZipInputStream in = new ZipInputStream(new BufferedInputStream(new URL(url).openConnection().getInputStream())); - + ZipInputStream in = new ZipInputStream( + new BufferedInputStream(new URL(url).openConnection().getInputStream())); ZipEntry entry; int cnt = 0; @@ -20,43 +42,43 @@ public static void downloaderZip(String url, File dest, String startsWith, Strin StringBuilder path = new StringBuilder(dest.getPath()).append('/').append(entry.getName()); File file = new File(path.toString()); - if(entry.isDirectory()){ + if(entry.isDirectory()) { file.mkdirs(); continue; } - if(entry.getName().startsWith(startsWith) && entry.getName().endsWith(endsWith)){ + if(entry.getName().startsWith(startsWith) && entry.getName().endsWith(endsWith)) { /* - AudioFormat format = new AudioFormat( AudioFormat.Encoding.PCM_SIGNED, 16000, 16, 1, 2, 16000, false); - int length = (int) Math.ceil((double) entry.getExtra().length / format.getFrameSize()); - AudioInputStream audio = new AudioInputStream(new ByteArrayInputStream(entry.getExtra()), format, length); - AudioSystem.write(audio, AudioFileFormat.Type.WAVE, file); - */ + * AudioFormat format = new AudioFormat( AudioFormat.Encoding.PCM_SIGNED, 16000, 16, 1, 2, 16000, + * false); int length = (int) Math.ceil((double) entry.getExtra().length / format.getFrameSize()); + * AudioInputStream audio = new AudioInputStream(new ByteArrayInputStream(entry.getExtra()), format, + * length); AudioSystem.write(audio, AudioFileFormat.Type.WAVE, file); + */ FileOutputStream out = new FileOutputStream(file); - for (int read = in.read(); read != -1; read = in.read()){ + for(int read = in.read(); read != -1; read = in.read()) { out.write(read); } out.close(); cnt++; - if(cnt%50 == 0){ + if(cnt % 50 == 0) { System.out.println(cnt + "/8008"); } // TODO: only for debugging - if(cnt == 200){ + if(cnt == 200) { break; } } } - } catch ( IOException e){ + } + catch(IOException e) { e.printStackTrace(); } - } } diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java index aa62d31d65d..779292a4ba6 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java @@ -19,18 +19,19 @@ package org.apache.sysds.runtime.matrix.data; -import org.apache.sysds.runtime.io.*; +import org.apache.commons.io.FileUtils; +import org.apache.sysds.runtime.io.ReaderWavFile; +import org.apache.sysds.runtime.io.DownloaderZip; import java.io.File; import java.io.IOException; - import java.util.List; import java.util.ArrayList; public class LibMatrixKeywordSpotting { - public static void main(String[] args) { + public static void main(String[] args) throws IOException { // download data String url = "http://storage.googleapis.com/download.tensorflow.org/data/mini_speech_commands.zip"; @@ -44,8 +45,11 @@ public static void main(String[] args) { List labels = new ArrayList<>(); List commands = new ArrayList<>(); - String sourceDir = "./tmp/mini_speech_commands"; - extractData(sourceDir, waves, labels, commands); + String sourceDirPath = "./tmp/mini_speech_commands"; + extractData(sourceDirPath, waves, labels, commands); + + // delete data + FileUtils.deleteDirectory(new File(sourceDirPath)); // TODO: // csv for waves @@ -55,14 +59,15 @@ public static void main(String[] args) { } - private static void extractData(String sourceDir, List waves, List labels, List commands) { + private static void extractData(String sourceDir, List waves, List labels, + List commands) { try { // get directory names getDirectories(sourceDir, commands); - for(String command : commands){ + for(String command : commands) { readWaveFiles(sourceDir, command, waves, labels, commands); } @@ -73,27 +78,28 @@ private static void extractData(String sourceDir, List waves, List commands) throws IOException { File[] subDirs = new File(sourceDir).listFiles(); - if(subDirs == null) return; + if(subDirs == null) + return; - for(File c: subDirs){ - if(c.isDirectory()){ + for(File c : subDirs) { + if(c.isDirectory()) { commands.add(c.getName()); } } } - private static void readWaveFiles(String sourceDir, String command, List waves, List labels, List commands){ + private static void readWaveFiles(String sourceDir, String command, List waves, List labels, + List commands) { String path = sourceDir + '/' + command; File dir = new File(path); File[] waveFiles = dir.listFiles(); - for(File file: waveFiles){ + for(File file : waveFiles) { waves.add(ReaderWavFile.readMonoAudioFromWavFile(file.getPath())); labels.add(commands.indexOf(command)); } From eaa6fbde0519782522cf68db7defa12152c23864 Mon Sep 17 00:00:00 2001 From: Jessica Priebe Date: Fri, 16 Feb 2024 22:16:01 +0100 Subject: [PATCH 110/133] added save to CSV --- .../sysds/runtime/io/DownloaderZip.java | 5 ++- .../matrix/data/LibMatrixKeywordSpotting.java | 38 ++++++++++++++++--- 2 files changed, 36 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/apache/sysds/runtime/io/DownloaderZip.java b/src/main/java/org/apache/sysds/runtime/io/DownloaderZip.java index e9765272fd5..106066efb4a 100644 --- a/src/main/java/org/apache/sysds/runtime/io/DownloaderZip.java +++ b/src/main/java/org/apache/sysds/runtime/io/DownloaderZip.java @@ -39,13 +39,14 @@ public static void downloaderZip(String url, File dest, String startsWith, Strin ZipEntry entry; int cnt = 0; while((entry = in.getNextEntry()) != null) { - StringBuilder path = new StringBuilder(dest.getPath()).append('/').append(entry.getName()); - File file = new File(path.toString()); + String path = dest.getPath() + '/' + entry.getName(); + File file = new File(path); if(entry.isDirectory()) { file.mkdirs(); continue; } + if(entry.getName().startsWith(startsWith) && entry.getName().endsWith(endsWith)) { /* diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java index 779292a4ba6..9e618f79653 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java @@ -25,8 +25,12 @@ import java.io.File; import java.io.IOException; +import java.io.FileWriter; +import java.io.PrintWriter; +import java.io.BufferedWriter; import java.util.List; +import java.util.Arrays; import java.util.ArrayList; public class LibMatrixKeywordSpotting { @@ -51,11 +55,9 @@ public static void main(String[] args) throws IOException { // delete data FileUtils.deleteDirectory(new File(sourceDirPath)); - // TODO: - // csv for waves - // csv for labels - use index? - // if we use index we also need a csv to translate index back to command - // don't forget magnitudes after stft! + saveToCSV("./tmp/waves", waves); + saveToCSV("./tmp/labels", labels); + saveToCSV("./tmp/commands", commands); } @@ -99,6 +101,9 @@ private static void readWaveFiles(String sourceDir, String command, List data) { + + try(PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(path)))) { + + for(Object elem : data) { + if(elem instanceof double[]) { + String str = Arrays.toString((double[]) elem); + // remove brackets + out.print(str.substring(1, str.length() - 1)); + } + else { + out.print(elem); + } + out.println(); + } + + } + catch(IOException e) { + e.printStackTrace(); + } + + } + } From 990bd6c6a4198d0298bdd53776382e004861e2b6 Mon Sep 17 00:00:00 2001 From: Jessica Priebe Date: Fri, 16 Feb 2024 22:23:24 +0100 Subject: [PATCH 111/133] removes debugging prints and added start and finish print for downloading --- .../org/apache/sysds/runtime/io/DownloaderZip.java | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/main/java/org/apache/sysds/runtime/io/DownloaderZip.java b/src/main/java/org/apache/sysds/runtime/io/DownloaderZip.java index 106066efb4a..6a6384697b4 100644 --- a/src/main/java/org/apache/sysds/runtime/io/DownloaderZip.java +++ b/src/main/java/org/apache/sysds/runtime/io/DownloaderZip.java @@ -36,8 +36,10 @@ public static void downloaderZip(String url, File dest, String startsWith, Strin ZipInputStream in = new ZipInputStream( new BufferedInputStream(new URL(url).openConnection().getInputStream())); - ZipEntry entry; int cnt = 0; + System.out.println("start downloading"); + + ZipEntry entry; while((entry = in.getNextEntry()) != null) { String path = dest.getPath() + '/' + entry.getName(); File file = new File(path); @@ -66,15 +68,12 @@ public static void downloaderZip(String url, File dest, String startsWith, Strin if(cnt % 50 == 0) { System.out.println(cnt + "/8008"); } - - // TODO: only for debugging - if(cnt == 200) { - break; - } } } + System.out.println("finished downloading"); + } catch(IOException e) { e.printStackTrace(); From a2bb64827fca95f74776d968268cb94f6abd03a2 Mon Sep 17 00:00:00 2001 From: Jessica Priebe Date: Sat, 17 Feb 2024 15:40:51 +0100 Subject: [PATCH 112/133] changed short/double to int --- .../apache/sysds/runtime/io/ReaderWavFile.java | 16 +++++++--------- .../matrix/data/LibMatrixKeywordSpotting.java | 13 +++++++------ 2 files changed, 14 insertions(+), 15 deletions(-) diff --git a/src/main/java/org/apache/sysds/runtime/io/ReaderWavFile.java b/src/main/java/org/apache/sysds/runtime/io/ReaderWavFile.java index 03beffe0101..1f7e9d79cef 100644 --- a/src/main/java/org/apache/sysds/runtime/io/ReaderWavFile.java +++ b/src/main/java/org/apache/sysds/runtime/io/ReaderWavFile.java @@ -29,12 +29,12 @@ public class ReaderWavFile { - public static double[] readMonoAudioFromWavFile(String filePath) { + public static int[] readMonoAudioFromWavFile(String filePath) { try { // open audio file AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(new File(filePath)); - double[] audioValues = readMonoAudioFromWavFile(audioInputStream); + int[] audioValues = readMonoAudioFromWavFile(audioInputStream); audioInputStream.close(); return audioValues; @@ -46,7 +46,7 @@ public static double[] readMonoAudioFromWavFile(String filePath) { } - public static double[] readMonoAudioFromWavFile(AudioInputStream audioInputStream) { + public static int[] readMonoAudioFromWavFile(AudioInputStream audioInputStream) { try { @@ -71,13 +71,11 @@ public static double[] readMonoAudioFromWavFile(AudioInputStream audioInputStrea } // convert byte array to double array - double[] audioValues = new double[numFrames]; + int[] audioValues = new int[numFrames]; for(int i = 0, frameIndex = 0; i < bytesRead; i += frameSize, frameIndex++) { - // 16-bit PCM encoding - // combine two bytes into a 16-bit integer (short) - short sampleValue = (short) ((audioData[i + 1] << 8) | (audioData[i] & 0xFF)); - // audio ranges from -32768 to 32767, normalize to range -1 to 1 - audioValues[frameIndex] = sampleValue / 32768.0; + // only use 8 most significant bits + int sampleValue = audioData[i + 1] << 8; + audioValues[frameIndex] = sampleValue; } // close mono audio stream diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java index 9e618f79653..b7e144edf37 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java @@ -45,7 +45,8 @@ public static void main(String[] args) throws IOException { DownloaderZip.downloaderZip(url, dest, startsWith, endsWith); // zip contains command folders which contain corresponding .wav files - List waves = new ArrayList<>(); + // TODO: write directly into the csv + List waves = new ArrayList<>(); List labels = new ArrayList<>(); List commands = new ArrayList<>(); @@ -53,7 +54,7 @@ public static void main(String[] args) throws IOException { extractData(sourceDirPath, waves, labels, commands); // delete data - FileUtils.deleteDirectory(new File(sourceDirPath)); + // FileUtils.deleteDirectory(new File(sourceDirPath)); saveToCSV("./tmp/waves", waves); saveToCSV("./tmp/labels", labels); @@ -61,7 +62,7 @@ public static void main(String[] args) throws IOException { } - private static void extractData(String sourceDir, List waves, List labels, + private static void extractData(String sourceDir, List waves, List labels, List commands) { try { @@ -94,7 +95,7 @@ private static void getDirectories(String sourceDir, List commands) thro } - private static void readWaveFiles(String sourceDir, String command, List waves, List labels, + private static void readWaveFiles(String sourceDir, String command, List waves, List labels, List commands) { String path = sourceDir + '/' + command; @@ -116,8 +117,8 @@ private static void saveToCSV(String path, List data) { try(PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(path)))) { for(Object elem : data) { - if(elem instanceof double[]) { - String str = Arrays.toString((double[]) elem); + if(elem instanceof int[]) { + String str = Arrays.toString((int[]) elem); // remove brackets out.print(str.substring(1, str.length() - 1)); } From 75f22cb33ff84e3a0ff061656b78e5889f03d316 Mon Sep 17 00:00:00 2001 From: Jessica Priebe Date: Thu, 15 Feb 2024 17:10:31 +0100 Subject: [PATCH 113/133] added TODO --- .../matrix/data/LibMatrixKeywordSpotting.java | 38 ++++++++++++------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java index b7e144edf37..499bd4f6803 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java @@ -112,27 +112,37 @@ private static void readWaveFiles(String sourceDir, String command, List } - private static void saveToCSV(String path, List data) { + private static void readWaveFiles(byte[] zipData, List dirs, List waves, List labels) + throws IOException { - try(PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter(path)))) { + ZipInputStream stream = new ZipInputStream(new ByteArrayInputStream(zipData)); + ZipEntry entry; + String dir = dirs.get(0); - for(Object elem : data) { - if(elem instanceof int[]) { - String str = Arrays.toString((int[]) elem); - // remove brackets - out.print(str.substring(1, str.length() - 1)); + while((entry = stream.getNextEntry()) != null) { + if(entry.getName().endsWith(".wav")) { + if(!entry.getName().contains(dir)){ + dir = findDir(entry, dirs); } - else { - out.print(elem); - } - out.println(); + // read file + // TODO: isn't working: we need an audioInputStream! + double[] data = ReaderWavFile.readMonoAudioFromWavFile(new ByteArrayInputStream(entry.getExtra())); + waves.add(data); + labels.add(dir); } - } - catch(IOException e) { - e.printStackTrace(); + + } + + private static String findDir(ZipEntry entry, List dirs){ + + for (String dir : dirs){ + if(entry.getName().startsWith(dir)){ + return dir; + } } + return null; } } From a3f8153ba6d2ce515e4004900fb0b873c10b505e Mon Sep 17 00:00:00 2001 From: Jessica Priebe Date: Fri, 16 Feb 2024 15:22:14 +0100 Subject: [PATCH 114/133] converted bytearrayinputstream to audioinoutstream --- .../runtime/matrix/data/LibMatrixKeywordSpotting.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java index 499bd4f6803..7f3eaf6b7ab 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java @@ -21,7 +21,9 @@ import org.apache.commons.io.FileUtils; import org.apache.sysds.runtime.io.ReaderWavFile; -import org.apache.sysds.runtime.io.DownloaderZip; + +import javax.sound.sampled.*; +import java.net.URL; import java.io.File; import java.io.IOException; @@ -126,7 +128,10 @@ private static void readWaveFiles(byte[] zipData, List dirs, List Date: Fri, 16 Feb 2024 20:56:58 +0100 Subject: [PATCH 115/133] fixed --- src/main/java/org/apache/sysds/runtime/io/ReaderWavFile.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/apache/sysds/runtime/io/ReaderWavFile.java b/src/main/java/org/apache/sysds/runtime/io/ReaderWavFile.java index 1f7e9d79cef..e32e602fb89 100644 --- a/src/main/java/org/apache/sysds/runtime/io/ReaderWavFile.java +++ b/src/main/java/org/apache/sysds/runtime/io/ReaderWavFile.java @@ -46,7 +46,7 @@ public static int[] readMonoAudioFromWavFile(String filePath) { } - public static int[] readMonoAudioFromWavFile(AudioInputStream audioInputStream) { + public static double[] readMonoAudioFromWavFile(AudioInputStream audioInputStream) { try { @@ -78,11 +78,14 @@ public static int[] readMonoAudioFromWavFile(AudioInputStream audioInputStream) audioValues[frameIndex] = sampleValue; } + // close mono audio stream // close mono audio stream monoAudioInputStream.close(); + return audioValues; } + catch(IOException e) { catch(IOException e) { e.printStackTrace(); return null; From f09e2f80e6d44d094afe8d3a9b55cdeedfd41976 Mon Sep 17 00:00:00 2001 From: Jessica Priebe Date: Sat, 17 Feb 2024 16:08:01 +0100 Subject: [PATCH 116/133] changed short/double to int --- .../sysds/runtime/io/ReaderWavFile.java | 9 +++-- .../matrix/data/LibMatrixKeywordSpotting.java | 35 +++++++++---------- 2 files changed, 24 insertions(+), 20 deletions(-) diff --git a/src/main/java/org/apache/sysds/runtime/io/ReaderWavFile.java b/src/main/java/org/apache/sysds/runtime/io/ReaderWavFile.java index e32e602fb89..2e6944210a7 100644 --- a/src/main/java/org/apache/sysds/runtime/io/ReaderWavFile.java +++ b/src/main/java/org/apache/sysds/runtime/io/ReaderWavFile.java @@ -29,12 +29,14 @@ public class ReaderWavFile { + public static int[] readMonoAudioFromWavFile(String filePath) { public static int[] readMonoAudioFromWavFile(String filePath) { try { // open audio file AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(new File(filePath)); int[] audioValues = readMonoAudioFromWavFile(audioInputStream); + int[] audioValues = readMonoAudioFromWavFile(audioInputStream); audioInputStream.close(); return audioValues; @@ -46,7 +48,7 @@ public static int[] readMonoAudioFromWavFile(String filePath) { } - public static double[] readMonoAudioFromWavFile(AudioInputStream audioInputStream) { + public static int[] readMonoAudioFromWavFile(AudioInputStream audioInputStream) { try { @@ -70,12 +72,15 @@ public static double[] readMonoAudioFromWavFile(AudioInputStream audioInputStrea return null; } - // convert byte array to double array + // convert byte array to int array int[] audioValues = new int[numFrames]; for(int i = 0, frameIndex = 0; i < bytesRead; i += frameSize, frameIndex++) { // only use 8 most significant bits int sampleValue = audioData[i + 1] << 8; audioValues[frameIndex] = sampleValue; + // only use 8 most significant bits + int sampleValue = audioData[i + 1] << 8; + audioValues[frameIndex] = sampleValue; } // close mono audio stream diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java index 7f3eaf6b7ab..5cdf81a2294 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java @@ -47,13 +47,9 @@ public static void main(String[] args) throws IOException { DownloaderZip.downloaderZip(url, dest, startsWith, endsWith); // zip contains command folders which contain corresponding .wav files - // TODO: write directly into the csv List waves = new ArrayList<>(); - List labels = new ArrayList<>(); - List commands = new ArrayList<>(); - - String sourceDirPath = "./tmp/mini_speech_commands"; - extractData(sourceDirPath, waves, labels, commands); + List labels = new ArrayList<>(); + loadAllData(url, waves, labels); // delete data // FileUtils.deleteDirectory(new File(sourceDirPath)); @@ -64,8 +60,7 @@ public static void main(String[] args) throws IOException { } - private static void extractData(String sourceDir, List waves, List labels, - List commands) { + private static void loadAllData(String url, List waves, List labels) { try { @@ -83,18 +78,22 @@ private static void extractData(String sourceDir, List waves, List commands) throws IOException { + private static byte[] getBytesZipFile(URL url) throws IOException { - File[] subDirs = new File(sourceDir).listFiles(); - if(subDirs == null) - return; + InputStream in = url.openConnection().getInputStream(); + // String zipFilePath = "./src/main/java/org/apache/sysds/runtime/matrix/data/mini_speech_commands_slimmed.zip"; + // InputStream in = new FileInputStream(zipFilePath); - for(File c : subDirs) { - if(c.isDirectory()) { - commands.add(c.getName()); - } + ByteArrayOutputStream out = new ByteArrayOutputStream(); + byte[] dataBuffer = new byte[1024]; + + int bytesRead; + while((bytesRead = in.read(dataBuffer, 0, 1024)) != -1) { + out.write(dataBuffer, 0, bytesRead); } + return out.toByteArray(); + } private static void readWaveFiles(String sourceDir, String command, List waves, List labels, @@ -114,7 +113,7 @@ private static void readWaveFiles(String sourceDir, String command, List } - private static void readWaveFiles(byte[] zipData, List dirs, List waves, List labels) + private static void readWaveFiles(byte[] zipData, List dirs, List waves, List labels) throws IOException { ZipInputStream stream = new ZipInputStream(new ByteArrayInputStream(zipData)); @@ -131,7 +130,7 @@ private static void readWaveFiles(byte[] zipData, List dirs, List Date: Sat, 17 Feb 2024 16:11:50 +0100 Subject: [PATCH 117/133] removed url --- .../matrix/data/LibMatrixKeywordSpotting.java | 26 +++++++++---------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java index 5cdf81a2294..aee6589a32a 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java @@ -37,19 +37,16 @@ public class LibMatrixKeywordSpotting { - public static void main(String[] args) throws IOException { - - // download data - String url = "http://storage.googleapis.com/download.tensorflow.org/data/mini_speech_commands.zip"; - File dest = new File("./tmp"); - String startsWith = "mini_speech_commands"; - String endsWith = ".wav"; - DownloaderZip.downloaderZip(url, dest, startsWith, endsWith); + /** + * Please download the zip file before running. + * Save it in "./tmp". + */ + public static void main(String[] args) { // zip contains command folders which contain corresponding .wav files List waves = new ArrayList<>(); List labels = new ArrayList<>(); - loadAllData(url, waves, labels); + loadAllData(waves, labels); // delete data // FileUtils.deleteDirectory(new File(sourceDirPath)); @@ -60,9 +57,11 @@ public static void main(String[] args) throws IOException { } - private static void loadAllData(String url, List waves, List labels) { + private static void loadAllData(List waves, List labels) { try { + // get zip data + byte[] zipData = getBytesZipFile(); // get directory names getDirectories(sourceDir, commands); @@ -78,11 +77,10 @@ private static void loadAllData(String url, List waves, List labe } - private static byte[] getBytesZipFile(URL url) throws IOException { + private static byte[] getBytesZipFile() throws IOException { - InputStream in = url.openConnection().getInputStream(); - // String zipFilePath = "./src/main/java/org/apache/sysds/runtime/matrix/data/mini_speech_commands_slimmed.zip"; - // InputStream in = new FileInputStream(zipFilePath); + String zipFilePath = "./src/main/java/org/apache/sysds/runtime/matrix/data/mini_speech_commands_slimmed.zip"; + InputStream in = new FileInputStream(zipFilePath); ByteArrayOutputStream out = new ByteArrayOutputStream(); byte[] dataBuffer = new byte[1024]; From 5267248cbc54fbed7670c39c8467bcbde9b984ac Mon Sep 17 00:00:00 2001 From: Jessica Priebe Date: Sat, 17 Feb 2024 17:33:20 +0100 Subject: [PATCH 118/133] changed logic --- .../sysds/runtime/io/ReaderWavFile.java | 23 --- .../matrix/data/LibMatrixKeywordSpotting.java | 140 ++++++++---------- .../data/mini_speech_commands_slimmed.zip | Bin 250932 -> 0 bytes 3 files changed, 63 insertions(+), 100 deletions(-) delete mode 100644 src/main/java/org/apache/sysds/runtime/matrix/data/mini_speech_commands_slimmed.zip diff --git a/src/main/java/org/apache/sysds/runtime/io/ReaderWavFile.java b/src/main/java/org/apache/sysds/runtime/io/ReaderWavFile.java index 2e6944210a7..25d55fdd474 100644 --- a/src/main/java/org/apache/sysds/runtime/io/ReaderWavFile.java +++ b/src/main/java/org/apache/sysds/runtime/io/ReaderWavFile.java @@ -19,39 +19,17 @@ package org.apache.sysds.runtime.io; -import java.io.File; import java.io.IOException; import javax.sound.sampled.AudioSystem; import javax.sound.sampled.AudioFormat; import javax.sound.sampled.AudioInputStream; -import javax.sound.sampled.UnsupportedAudioFileException; public class ReaderWavFile { - public static int[] readMonoAudioFromWavFile(String filePath) { - public static int[] readMonoAudioFromWavFile(String filePath) { - - try { - // open audio file - AudioInputStream audioInputStream = AudioSystem.getAudioInputStream(new File(filePath)); - int[] audioValues = readMonoAudioFromWavFile(audioInputStream); - int[] audioValues = readMonoAudioFromWavFile(audioInputStream); - audioInputStream.close(); - return audioValues; - - } - catch(UnsupportedAudioFileException | IOException e) { - e.printStackTrace(); - return null; - } - - } - public static int[] readMonoAudioFromWavFile(AudioInputStream audioInputStream) { try { - // collapse channels to mono channel int channels = 1; AudioFormat monoAudioFormat = new AudioFormat(audioInputStream.getFormat().getSampleRate(), @@ -88,7 +66,6 @@ public static int[] readMonoAudioFromWavFile(AudioInputStream audioInputStream) monoAudioInputStream.close(); return audioValues; - } catch(IOException e) { catch(IOException e) { diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java index aee6589a32a..709922d6045 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java @@ -22,54 +22,37 @@ import org.apache.commons.io.FileUtils; import org.apache.sysds.runtime.io.ReaderWavFile; -import javax.sound.sampled.*; -import java.net.URL; +import javax.sound.sampled.AudioFormat; +import javax.sound.sampled.AudioInputStream; -import java.io.File; import java.io.IOException; +import java.io.FileInputStream; +import java.io.ByteArrayInputStream; import java.io.FileWriter; import java.io.PrintWriter; import java.io.BufferedWriter; import java.util.List; import java.util.Arrays; +import java.util.Arrays; import java.util.ArrayList; public class LibMatrixKeywordSpotting { /** - * Please download the zip file before running. - * Save it in "./tmp". + * Please download the + * zip file before + * running. Save it in "./tmp". */ public static void main(String[] args) { - // zip contains command folders which contain corresponding .wav files - List waves = new ArrayList<>(); - List labels = new ArrayList<>(); - loadAllData(waves, labels); - - // delete data - // FileUtils.deleteDirectory(new File(sourceDirPath)); - - saveToCSV("./tmp/waves", waves); - saveToCSV("./tmp/labels", labels); - saveToCSV("./tmp/commands", commands); - - } - - private static void loadAllData(List waves, List labels) { + String basePath = "./tmp/"; + String zipPath = basePath + "mini_speech_commands.zip"; try { // get zip data - byte[] zipData = getBytesZipFile(); - - // get directory names - getDirectories(sourceDir, commands); - - for(String command : commands) { - readWaveFiles(sourceDir, command, waves, labels, commands); - } - + ZipInputStream zipStream = new ZipInputStream(new FileInputStream(zipPath)); + saveDataToCSV(basePath, zipStream); } catch(IOException e) { e.printStackTrace(); @@ -77,74 +60,77 @@ private static void loadAllData(List waves, List labels) { } - private static byte[] getBytesZipFile() throws IOException { - - String zipFilePath = "./src/main/java/org/apache/sysds/runtime/matrix/data/mini_speech_commands_slimmed.zip"; - InputStream in = new FileInputStream(zipFilePath); - - ByteArrayOutputStream out = new ByteArrayOutputStream(); - byte[] dataBuffer = new byte[1024]; + private static void saveDataToCSV(String basePath, ZipInputStream zipStream) throws IOException { - int bytesRead; - while((bytesRead = in.read(dataBuffer, 0, 1024)) != -1) { - out.write(dataBuffer, 0, bytesRead); - } - - return out.toByteArray(); - - } + PrintWriter commandsCSV = new PrintWriter(new BufferedWriter(new FileWriter(basePath + "commands"))); + PrintWriter wavesCSV = new PrintWriter(new BufferedWriter(new FileWriter(basePath + "waves"))); + PrintWriter labelsCSV = new PrintWriter(new BufferedWriter(new FileWriter(basePath + "labels"))); - private static void readWaveFiles(String sourceDir, String command, List waves, List labels, - List commands) { + List commands = new ArrayList<>(); - String path = sourceDir + '/' + command; - File dir = new File(path); + // exclude main directory + ZipEntry entry = zipStream.getNextEntry(); - File[] waveFiles = dir.listFiles(); - if(waveFiles == null) + if(entry == null) return; + String mainDir = entry.getName(); - for(File file : waveFiles) { - waves.add(ReaderWavFile.readMonoAudioFromWavFile(file.getPath())); - labels.add(commands.indexOf(command)); - } + while((entry = zipStream.getNextEntry()) != null) { - } + if(entry.isDirectory()) { - private static void readWaveFiles(byte[] zipData, List dirs, List waves, List labels) - throws IOException { + String dir = entry.getName(); + // remove "/" at the end + String name = dir.substring(mainDir.length(), dir.length() - 1); - ZipInputStream stream = new ZipInputStream(new ByteArrayInputStream(zipData)); - ZipEntry entry; - String dir = dirs.get(0); + commands.add(name); + // save to csv + commandsCSV.print(name); + commandsCSV.println(); + + } + else if(isWavFileToProcess(entry)) { - while((entry = stream.getNextEntry()) != null) { - if(entry.getName().endsWith(".wav")) { - if(!entry.getName().contains(dir)){ - dir = findDir(entry, dirs); - } // read file - // TODO: isn't working: we need an audioInputStream! - AudioFormat format = new AudioFormat( AudioFormat.Encoding.PCM_SIGNED, 16000, 16, 1, 2, 16000, false); + AudioFormat format = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, 16000, 16, 1, 2, 16000, false); int length = (int) Math.ceil((double) entry.getExtra().length / format.getFrameSize()); - AudioInputStream audio = new AudioInputStream(new ByteArrayInputStream(entry.getExtra()), format, length); + AudioInputStream audio = new AudioInputStream(new ByteArrayInputStream(entry.getExtra()), format, + length); int[] data = ReaderWavFile.readMonoAudioFromWavFile(audio); - waves.add(data); - labels.add(dir); + + // save to csv + String str = Arrays.toString(data); + wavesCSV.print(str.substring(1, str.length() - 1)); + wavesCSV.println(); + + labelsCSV.print(commands.indexOf(getCommand(entry))); + labelsCSV.println(); } } } - private static String findDir(ZipEntry entry, List dirs){ + private static boolean isWavFileToProcess(ZipEntry entry) { - for (String dir : dirs){ - if(entry.getName().startsWith(dir)){ - return dir; - } - } + String path = entry.getName(); + + if(!path.endsWith(".wav")) + return false; + + int end = path.lastIndexOf('/'); + String file = path.substring(end + 1); + + return !file.startsWith("."); + } + + private static String getCommand(ZipEntry entry) { + + String path = entry.getName(); + + int end = path.lastIndexOf('/'); + int start = path.substring(0, end).indexOf('/'); - return null; + return path.substring(start + 1, end); } } diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/mini_speech_commands_slimmed.zip b/src/main/java/org/apache/sysds/runtime/matrix/data/mini_speech_commands_slimmed.zip deleted file mode 100644 index bd0cee4c1fe81a157f57a543ff9cd7d57beceeca..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 250932 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%0AQ0=*+L1VZ@PLL>zsEkfl(+;n%rEGlt5{ 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

      g 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 From 09356ef92381f35f5f8aaedd0dd80c31546af1cd Mon Sep 17 00:00:00 2001 From: Jessica Priebe Date: Sat, 17 Feb 2024 17:40:00 +0100 Subject: [PATCH 119/133] fixed writer --- .../sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java index 709922d6045..0cf3e882654 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java @@ -108,6 +108,10 @@ else if(isWavFileToProcess(entry)) { } } + commandsCSV.close(); + labelsCSV.close(); + wavesCSV.close(); + } private static boolean isWavFileToProcess(ZipEntry entry) { From 5e9e19584cd2e9e2806a6cc462526bea6d90bf51 Mon Sep 17 00:00:00 2001 From: Jessica Priebe Date: Sat, 17 Feb 2024 17:43:38 +0100 Subject: [PATCH 120/133] merged application_zipstream into application --- .../sysds/runtime/io/DownloaderZip.java | 84 ------------------- 1 file changed, 84 deletions(-) delete mode 100644 src/main/java/org/apache/sysds/runtime/io/DownloaderZip.java diff --git a/src/main/java/org/apache/sysds/runtime/io/DownloaderZip.java b/src/main/java/org/apache/sysds/runtime/io/DownloaderZip.java deleted file mode 100644 index 6a6384697b4..00000000000 --- a/src/main/java/org/apache/sysds/runtime/io/DownloaderZip.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package org.apache.sysds.runtime.io; - -import java.io.File; -import java.io.IOException; -import java.io.BufferedInputStream; -import java.io.FileOutputStream; - -import java.net.URL; -import java.util.zip.ZipEntry; -import java.util.zip.ZipInputStream; - -public class DownloaderZip { - - public static void downloaderZip(String url, File dest, String startsWith, String endsWith) { - - try { - ZipInputStream in = new ZipInputStream( - new BufferedInputStream(new URL(url).openConnection().getInputStream())); - - int cnt = 0; - System.out.println("start downloading"); - - ZipEntry entry; - while((entry = in.getNextEntry()) != null) { - String path = dest.getPath() + '/' + entry.getName(); - File file = new File(path); - - if(entry.isDirectory()) { - file.mkdirs(); - continue; - } - - if(entry.getName().startsWith(startsWith) && entry.getName().endsWith(endsWith)) { - - /* - * AudioFormat format = new AudioFormat( AudioFormat.Encoding.PCM_SIGNED, 16000, 16, 1, 2, 16000, - * false); int length = (int) Math.ceil((double) entry.getExtra().length / format.getFrameSize()); - * AudioInputStream audio = new AudioInputStream(new ByteArrayInputStream(entry.getExtra()), format, - * length); AudioSystem.write(audio, AudioFileFormat.Type.WAVE, file); - */ - - FileOutputStream out = new FileOutputStream(file); - for(int read = in.read(); read != -1; read = in.read()) { - out.write(read); - } - out.close(); - - cnt++; - if(cnt % 50 == 0) { - System.out.println(cnt + "/8008"); - } - } - - } - - System.out.println("finished downloading"); - - } - catch(IOException e) { - e.printStackTrace(); - } - - } - -} From 3fc6e4d7a4d54096700766b4a90a6278857f8324 Mon Sep 17 00:00:00 2001 From: Jessica Priebe Date: Sat, 17 Feb 2024 19:00:23 +0100 Subject: [PATCH 121/133] added suffix --- .../sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java index 0cf3e882654..460a3f9afa6 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java @@ -62,9 +62,9 @@ public static void main(String[] args) { private static void saveDataToCSV(String basePath, ZipInputStream zipStream) throws IOException { - PrintWriter commandsCSV = new PrintWriter(new BufferedWriter(new FileWriter(basePath + "commands"))); - PrintWriter wavesCSV = new PrintWriter(new BufferedWriter(new FileWriter(basePath + "waves"))); - PrintWriter labelsCSV = new PrintWriter(new BufferedWriter(new FileWriter(basePath + "labels"))); + PrintWriter commandsCSV = new PrintWriter(new BufferedWriter(new FileWriter(basePath + "commands.csv"))); + PrintWriter wavesCSV = new PrintWriter(new BufferedWriter(new FileWriter(basePath + "waves.csv"))); + PrintWriter labelsCSV = new PrintWriter(new BufferedWriter(new FileWriter(basePath + "labels.csv"))); List commands = new ArrayList<>(); From be71987541329b7ea17b8e382e459959ef338fea Mon Sep 17 00:00:00 2001 From: mufwan Date: Mon, 12 Feb 2024 17:36:38 +0100 Subject: [PATCH 122/133] stft dml --- .../parser/BuiltinFunctionExpression.java | 31 +++++++++++++++++++ .../cp/MultiReturnBuiltinCPInstruction.java | 8 +++++ .../scripts/functions/unary/matrix/fft.dml | 3 ++ 3 files changed, 42 insertions(+) create mode 100644 src/test/scripts/functions/unary/matrix/fft.dml diff --git a/src/main/java/org/apache/sysds/parser/BuiltinFunctionExpression.java b/src/main/java/org/apache/sysds/parser/BuiltinFunctionExpression.java index aef51a2fc12..ad6a36cc0cc 100644 --- a/src/main/java/org/apache/sysds/parser/BuiltinFunctionExpression.java +++ b/src/main/java/org/apache/sysds/parser/BuiltinFunctionExpression.java @@ -579,6 +579,37 @@ else if(expressionOne != null){ break; } + case STFT: + { + checkNumParameters(3); + checkMatrixParam(getFirstExpr()); + + // setup output properties + DataIdentifier stftOut1 = (DataIdentifier) getOutputs()[0]; + DataIdentifier stftOut2 = (DataIdentifier) getOutputs()[1]; + + // if (getFirstExpr().getOutput().getDim2() != 1 || getFirstExpr().getOutput().getDim2() != 2) { + // raiseValidateError("Eigen Decomposition can only be done on a square matrix. Input matrix is rectangular (rows=" + getFirstExpr().getOutput().getDim1() + ", cols="+ getFirstExpr().getOutput().getDim2() +")", conditional); + // } + + checkMatrixParam(getSecondExpr()); + checkMatrixParam(getThirdExpr()); + + // Output1 - stft Values + stftOut1.setDataType(DataType.MATRIX); + stftOut1.setValueType(ValueType.FP64); + stftOut1.setDimensions(getFirstExpr().getOutput().getDim1(), getFirstExpr().getOutput().getDim2()); + stftOut1.setBlocksize(getFirstExpr().getOutput().getBlocksize()); + + // Output2 - stft Vectors + stftOut2.setDataType(DataType.MATRIX); + stftOut2.setValueType(ValueType.FP64); + stftOut2.setDimensions(getFirstExpr().getOutput().getDim1(), getFirstExpr().getOutput().getDim2()); + stftOut2.setBlocksize(getFirstExpr().getOutput().getBlocksize()); + + break; + + } case REMOVE: { checkNumParameters(2); checkListParam(getFirstExpr()); diff --git a/src/main/java/org/apache/sysds/runtime/instructions/cp/MultiReturnBuiltinCPInstruction.java b/src/main/java/org/apache/sysds/runtime/instructions/cp/MultiReturnBuiltinCPInstruction.java index 0eca433090c..5b7c57a725d 100644 --- a/src/main/java/org/apache/sysds/runtime/instructions/cp/MultiReturnBuiltinCPInstruction.java +++ b/src/main/java/org/apache/sysds/runtime/instructions/cp/MultiReturnBuiltinCPInstruction.java @@ -133,6 +133,14 @@ else if ( opcode.equalsIgnoreCase("stft") ) { return new MultiReturnBuiltinCPInstruction(null, in1, outputs, opcode, str); } + else if ( opcode.equalsIgnoreCase("stft") ) { + // one input and two outputs + CPOperand in1 = new CPOperand(parts[1]); + outputs.add ( new CPOperand(parts[2], ValueType.FP64, DataType.MATRIX) ); + outputs.add ( new CPOperand(parts[3], ValueType.FP64, DataType.MATRIX) ); + + return new MultiReturnBuiltinCPInstruction(null, in1, outputs, opcode, str); + } else if ( opcode.equalsIgnoreCase("svd") ) { CPOperand in1 = new CPOperand(parts[1]); diff --git a/src/test/scripts/functions/unary/matrix/fft.dml b/src/test/scripts/functions/unary/matrix/fft.dml new file mode 100644 index 00000000000..9350001cb6d --- /dev/null +++ b/src/test/scripts/functions/unary/matrix/fft.dml @@ -0,0 +1,3 @@ + +print("hello") M = matrix("0 18 -15 3", rows=1, cols=4) [r,i] = fft(M) +print(toString(r)) print(toString(i)) \ No newline at end of file From b5fe74120fbbc1f0efe091bf7c99729bfe016f1d Mon Sep 17 00:00:00 2001 From: mufwan Date: Tue, 13 Feb 2024 01:30:39 +0100 Subject: [PATCH 123/133] stft dml integration --- fft.dml | 5 +++++ .../org/apache/sysds/parser/BuiltinFunctionExpression.java | 5 ++--- .../cp/MultiReturnComplexMatrixBuiltinCPInstruction.java | 2 +- 3 files changed, 8 insertions(+), 4 deletions(-) create mode 100644 fft.dml diff --git a/fft.dml b/fft.dml new file mode 100644 index 00000000000..d5186897d09 --- /dev/null +++ b/fft.dml @@ -0,0 +1,5 @@ +print("hello") +M = matrix("0 18 -15 3", rows=1, cols=4) +[r,i] = fft(M) +print(toString(r)) +print(toString(i)) \ No newline at end of file diff --git a/src/main/java/org/apache/sysds/parser/BuiltinFunctionExpression.java b/src/main/java/org/apache/sysds/parser/BuiltinFunctionExpression.java index ad6a36cc0cc..7264f17b470 100644 --- a/src/main/java/org/apache/sysds/parser/BuiltinFunctionExpression.java +++ b/src/main/java/org/apache/sysds/parser/BuiltinFunctionExpression.java @@ -581,7 +581,6 @@ else if(expressionOne != null){ } case STFT: { - checkNumParameters(3); checkMatrixParam(getFirstExpr()); // setup output properties @@ -592,8 +591,8 @@ else if(expressionOne != null){ // raiseValidateError("Eigen Decomposition can only be done on a square matrix. Input matrix is rectangular (rows=" + getFirstExpr().getOutput().getDim1() + ", cols="+ getFirstExpr().getOutput().getDim2() +")", conditional); // } - checkMatrixParam(getSecondExpr()); - checkMatrixParam(getThirdExpr()); + //checkMatrixParam(getSecondExpr()); + //checkMatrixParam(getThirdExpr()); // Output1 - stft Values stftOut1.setDataType(DataType.MATRIX); diff --git a/src/main/java/org/apache/sysds/runtime/instructions/cp/MultiReturnComplexMatrixBuiltinCPInstruction.java b/src/main/java/org/apache/sysds/runtime/instructions/cp/MultiReturnComplexMatrixBuiltinCPInstruction.java index 0c50fd6dc69..3cdc46b0a1d 100644 --- a/src/main/java/org/apache/sysds/runtime/instructions/cp/MultiReturnComplexMatrixBuiltinCPInstruction.java +++ b/src/main/java/org/apache/sysds/runtime/instructions/cp/MultiReturnComplexMatrixBuiltinCPInstruction.java @@ -107,7 +107,7 @@ public static MultiReturnComplexMatrixBuiltinCPInstruction parseInstruction(Stri outputs.add(new CPOperand(parts[3], ValueType.FP64, DataType.MATRIX)); return new MultiReturnComplexMatrixBuiltinCPInstruction(null, in1, outputs, opcode, str); - } + } else if (parts.length == 6 &&opcode.equalsIgnoreCase("stft")) { CPOperand in1 = new CPOperand(parts[1]); CPOperand windowSize = new CPOperand(parts[2]); From 2da7ad327b4f514385137ad39d6ecbc5a512d0bb Mon Sep 17 00:00:00 2001 From: mufwan Date: Wed, 14 Feb 2024 23:39:01 +0100 Subject: [PATCH 124/133] handle zero matrix --- .../parser/BuiltinFunctionExpression.java | 37 ++++++++++++++----- .../runtime/matrix/data/LibCommonsMath.java | 20 ++++++++++ 2 files changed, 48 insertions(+), 9 deletions(-) diff --git a/src/main/java/org/apache/sysds/parser/BuiltinFunctionExpression.java b/src/main/java/org/apache/sysds/parser/BuiltinFunctionExpression.java index 7264f17b470..ed3f7ec2005 100644 --- a/src/main/java/org/apache/sysds/parser/BuiltinFunctionExpression.java +++ b/src/main/java/org/apache/sysds/parser/BuiltinFunctionExpression.java @@ -357,7 +357,7 @@ public void validateExpression(MultiAssignmentStatement stmt, HashMap 0) { + raiseValidateError("Missing argument for function " + this.getOpCode(), false, + LanguageErrorCodes.INVALID_PARAMETERS); + } else if (getFifthExpr() != null) { + raiseValidateError("Invalid number of arguments for function " + this.getOpCode().toString().toLowerCase() + + "(). This function only takes 3 or 4 arguments.", false); + } else if (_args.length == 3) { + checkScalarParam(getSecondExpr()); + checkScalarParam(getThirdExpr()); + if (!isPowerOfTwo(((ConstIdentifier) getSecondExpr().getOutput()).getLongValue())) { + raiseValidateError("This FFT implementation is only defined for matrices with dimensions that are powers of 2." + + "The window size (2nd argument) is not a power of two", false, LanguageErrorCodes.INVALID_PARAMETERS); + } + } else if (_args.length == 4) { + checkMatrixParam(getSecondExpr()); + checkScalarParam(getThirdExpr()); + checkScalarParam(getFourthExpr()); + if (!isPowerOfTwo(((ConstIdentifier) getThirdExpr().getOutput()).getLongValue())) { + raiseValidateError("This FFT implementation is only defined for matrices with dimensions that are powers of 2." + + "The window size (3rd argument) is not a power of two", false, LanguageErrorCodes.INVALID_PARAMETERS); + } else if (getFirstExpr().getOutput().getDim2() != getSecondExpr().getOutput().getDim2()) { + raiseValidateError("The real and imaginary part of the provided matrix are of different dimensions.", false); + } else if (getFirstExpr().getOutput().getDim1() != 1 || getSecondExpr().getOutput().getDim1() != 1) { + raiseValidateError("This FFT implementation is only defined for one-dimensional matrices.", false, LanguageErrorCodes.INVALID_PARAMETERS); + } + } + // setup output properties DataIdentifier stftOut1 = (DataIdentifier) getOutputs()[0]; DataIdentifier stftOut2 = (DataIdentifier) getOutputs()[1]; - // if (getFirstExpr().getOutput().getDim2() != 1 || getFirstExpr().getOutput().getDim2() != 2) { - // raiseValidateError("Eigen Decomposition can only be done on a square matrix. Input matrix is rectangular (rows=" + getFirstExpr().getOutput().getDim1() + ", cols="+ getFirstExpr().getOutput().getDim2() +")", conditional); - // } - - //checkMatrixParam(getSecondExpr()); - //checkMatrixParam(getThirdExpr()); - // Output1 - stft Values stftOut1.setDataType(DataType.MATRIX); stftOut1.setValueType(ValueType.FP64); @@ -607,7 +627,6 @@ else if(expressionOne != null){ stftOut2.setBlocksize(getFirstExpr().getOutput().getBlocksize()); break; - } case REMOVE: { checkNumParameters(2); diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java index 6a25b6aec97..3cad54abe8b 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java @@ -349,6 +349,16 @@ private static MatrixBlock[] computeFFT(MatrixBlock re, int threads) { return fft(re, threads); } + private static boolean isMatrixAllZeros(MatrixBlock matrix) { + // Fast check for sparse representation + if (matrix.isInSparseFormat()) { + return matrix.getNonZeros() == 0; + } + // Dense format check + double sum = matrix.sum(); + return sum == 0; + } + /** * Function to perform IFFT on a given matrix. * @@ -495,6 +505,16 @@ private static MatrixBlock[] computeSTFT(MatrixBlock re, int windowSize, int ove return computeSTFT(re, null, windowSize, overlap, threads); } + /** + * Function to perform STFT on a given matrix. + * + * @param re matrix object + * @return array of matrix blocks + */ + private static MatrixBlock[] computeSTFT(MatrixBlock re, int windowSize, int overlap, int threads) { + return computeSTFT(re, null, windowSize, overlap, threads); + } + /** * Performs Singular Value Decomposition. Calls Apache Commons Math SVD. * X = U * Sigma * Vt, where X is the input matrix, From ed32efc3329bac813b4246181da1b3fb56d5d95a Mon Sep 17 00:00:00 2001 From: mufwan Date: Sat, 17 Feb 2024 20:45:23 +0100 Subject: [PATCH 125/133] update stft --- .../sysds/runtime/matrix/data/LibCommonsMath.java | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java index 3cad54abe8b..da128c3d8c8 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java @@ -345,8 +345,8 @@ private static MatrixBlock[] computeFFT(MatrixBlock re, int threads) { return new MatrixBlock[]{re, new MatrixBlock(re.getNumRows(), re.getNumColumns(), true)}; // Assuming you need to return two matrices: the real part and an imaginary part initialized to 0. } // run fft - re.sparseToDense(); - return fft(re, threads); + in.sparseToDense(); + return fft(in); } private static boolean isMatrixAllZeros(MatrixBlock matrix) { @@ -505,16 +505,6 @@ private static MatrixBlock[] computeSTFT(MatrixBlock re, int windowSize, int ove return computeSTFT(re, null, windowSize, overlap, threads); } - /** - * Function to perform STFT on a given matrix. - * - * @param re matrix object - * @return array of matrix blocks - */ - private static MatrixBlock[] computeSTFT(MatrixBlock re, int windowSize, int overlap, int threads) { - return computeSTFT(re, null, windowSize, overlap, threads); - } - /** * Performs Singular Value Decomposition. Calls Apache Commons Math SVD. * X = U * Sigma * Vt, where X is the input matrix, From 985643bcc769e21cb1dbd526ccc6afe78b496564 Mon Sep 17 00:00:00 2001 From: mufwan Date: Sat, 17 Feb 2024 20:49:18 +0100 Subject: [PATCH 126/133] branch to merge with application --- dml_test/keywordSpotting.dml | 4 ++-- dml_test/stft.dml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/dml_test/keywordSpotting.dml b/dml_test/keywordSpotting.dml index f9a27ab16e6..df268896b75 100644 --- a/dml_test/keywordSpotting.dml +++ b/dml_test/keywordSpotting.dml @@ -1,7 +1,7 @@ -X = read("shuffled_features.csv", format="csv") -y = read("shuffled_labels_minus_and_plus.csv", format="csv") +X = read("features.csv", format="csv") +y = read("labels.csv", format="csv") # Add a train test split. [X_train, X_test, y_train, y_test] = split(X=X, Y=y, seed= 13) diff --git a/dml_test/stft.dml b/dml_test/stft.dml index f9d224ce29c..1095abe0258 100644 --- a/dml_test/stft.dml +++ b/dml_test/stft.dml @@ -1,5 +1,5 @@ print("hello") -M = matrix("0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15", rows=4, cols=4) +M = matrix("0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15", rows=2, cols=8) I = matrix("0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0", rows=2, cols=8) [r,i] = stft(M, 4, 1) print(toString(r)) From a55190f9a84034946f9b978053a4cf5f61cbc7f4 Mon Sep 17 00:00:00 2001 From: mufwan Date: Sun, 18 Feb 2024 15:28:49 +0100 Subject: [PATCH 127/133] moved keywordSpotting.dml to scripts --- dml_test/fft.dml | 5 ----- dml_test/hello.dml | 1 - dml_test/stft.dml | 6 ------ {dml_test => scripts}/keywordSpotting.dml | 4 ++-- .../runtime/matrix/data/LibMatrixKeywordSpotting.java | 8 ++++---- 5 files changed, 6 insertions(+), 18 deletions(-) delete mode 100644 dml_test/fft.dml delete mode 100644 dml_test/hello.dml delete mode 100644 dml_test/stft.dml rename {dml_test => scripts}/keywordSpotting.dml (89%) diff --git a/dml_test/fft.dml b/dml_test/fft.dml deleted file mode 100644 index d5186897d09..00000000000 --- a/dml_test/fft.dml +++ /dev/null @@ -1,5 +0,0 @@ -print("hello") -M = matrix("0 18 -15 3", rows=1, cols=4) -[r,i] = fft(M) -print(toString(r)) -print(toString(i)) \ No newline at end of file diff --git a/dml_test/hello.dml b/dml_test/hello.dml deleted file mode 100644 index dfe80e8de72..00000000000 --- a/dml_test/hello.dml +++ /dev/null @@ -1 +0,0 @@ -print("Hello World"); \ No newline at end of file diff --git a/dml_test/stft.dml b/dml_test/stft.dml deleted file mode 100644 index 1095abe0258..00000000000 --- a/dml_test/stft.dml +++ /dev/null @@ -1,6 +0,0 @@ -print("hello") -M = matrix("0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15", rows=2, cols=8) -I = matrix("0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0", rows=2, cols=8) -[r,i] = stft(M, 4, 1) -print(toString(r)) -print(toString(i)) diff --git a/dml_test/keywordSpotting.dml b/scripts/keywordSpotting.dml similarity index 89% rename from dml_test/keywordSpotting.dml rename to scripts/keywordSpotting.dml index df268896b75..2c6383e5773 100644 --- a/dml_test/keywordSpotting.dml +++ b/scripts/keywordSpotting.dml @@ -1,7 +1,7 @@ -X = read("features.csv", format="csv") -y = read("labels.csv", format="csv") +X = read("tmp/shuffled_features.csv", format="csv") +y = read("tmp/shuffled_labels_minus_and_plus.csv", format="csv") # Add a train test split. [X_train, X_test, y_train, y_test] = split(X=X, Y=y, seed= 13) diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java index 460a3f9afa6..a6509dfe7dc 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java @@ -62,9 +62,9 @@ public static void main(String[] args) { private static void saveDataToCSV(String basePath, ZipInputStream zipStream) throws IOException { - PrintWriter commandsCSV = new PrintWriter(new BufferedWriter(new FileWriter(basePath + "commands.csv"))); - PrintWriter wavesCSV = new PrintWriter(new BufferedWriter(new FileWriter(basePath + "waves.csv"))); - PrintWriter labelsCSV = new PrintWriter(new BufferedWriter(new FileWriter(basePath + "labels.csv"))); + PrintWriter commandsCSV = new PrintWriter(new BufferedWriter(new FileWriter(basePath + "commands1.csv"))); + PrintWriter wavesCSV = new PrintWriter(new BufferedWriter(new FileWriter(basePath + "waves1.csv"))); + PrintWriter labelsCSV = new PrintWriter(new BufferedWriter(new FileWriter(basePath + "labels1.csv"))); List commands = new ArrayList<>(); @@ -95,7 +95,7 @@ else if(isWavFileToProcess(entry)) { AudioFormat format = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, 16000, 16, 1, 2, 16000, false); int length = (int) Math.ceil((double) entry.getExtra().length / format.getFrameSize()); AudioInputStream audio = new AudioInputStream(new ByteArrayInputStream(entry.getExtra()), format, - length); + length); int[] data = ReaderWavFile.readMonoAudioFromWavFile(audio); // save to csv From 9a2bc99263b2979c7fe99f29001ee25fb1a38f07 Mon Sep 17 00:00:00 2001 From: mufwan Date: Sun, 18 Feb 2024 17:55:33 +0100 Subject: [PATCH 128/133] add shuffle --- scripts/keywordSpotting.dml | 17 ++++++++++--- .../runtime/matrix/data/LibMatrixFourier.java | 1 + .../runtime/matrix/data/LibMatrixSTFT.java | 25 ++++++++++++++++++- .../sysds/test/component/matrix/STFTTest.java | 6 +++++ 4 files changed, 45 insertions(+), 4 deletions(-) diff --git a/scripts/keywordSpotting.dml b/scripts/keywordSpotting.dml index 2c6383e5773..15bd3589e86 100644 --- a/scripts/keywordSpotting.dml +++ b/scripts/keywordSpotting.dml @@ -3,8 +3,16 @@ X = read("tmp/shuffled_features.csv", format="csv") y = read("tmp/shuffled_labels_minus_and_plus.csv", format="csv") -# Add a train test split. -[X_train, X_test, y_train, y_test] = split(X=X, Y=y, seed= 13) +Xy = cbind(X, y) +num_col = ncol(Xy) +y_rand = rand(rows=nrow(Xy), cols=1, min=0, max=1, pdf="uniform") +Xy_shuffled = order(target = cbind(Xy, y_rand), by = num_col + 1) +Xy_shuffled = Xy_shuffled[,1:num_col] # Remove the random column +X_shuffled = Xy_shuffled[,1:(num_col-1)] # Features, excluding the last column which is labels +y_shuffled = Xy_shuffled[,num_col] # Labels, the last column + + +[X_train, X_test, y_train, y_test] = split(X=X_shuffled, Y=y_shuffled, seed= 13) # Preprocess with fft Optionally move before split. [X_train_re, X_train_im] = stft(X_train, 256, 128) @@ -27,7 +35,7 @@ sum_X_test_sq = X_test_re_sq + X_test_im_sq magnitudes_train = sqrt(sum_X_train_sq) magnitudes_test = sqrt(sum_X_test_sq) -bias = lm(X=magnitudes_train, y=y_train, reg=1e-3, maxi=5, verbose=TRUE) +bias = lm(X=magnitudes_train, y=y_train, reg=1e-1, maxi=10, verbose=TRUE) predictions = lmPredict(X=magnitudes_test, B=bias, verbose=FALSE) sign_predictions = sign(predictions) @@ -39,3 +47,6 @@ accuracy = correct / total #print(toString(predictions)) print(toString(accuracy)) + +R = lmPredictStats(yhat=predictions, ytest=y_test, lm=TRUE) +print(toString(R)) diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java index 87afcfb2817..7b2781c111b 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixFourier.java @@ -476,4 +476,5 @@ private static void limitCache(HashMap cache) { } } + } diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixSTFT.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixSTFT.java index edea46bea33..79e8df8eed6 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixSTFT.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixSTFT.java @@ -62,6 +62,8 @@ public static MatrixBlock[] stft(MatrixBlock re, MatrixBlock im, int windowSize, double[] re_inter = new double[out_len]; double[] im_inter = new double[out_len]; + ExecutorService pool = CommonThreadPool.get(1); + final ExecutorService pool = CommonThreadPool.get(threads); final List> tasks = new ArrayList<>(); @@ -91,6 +93,28 @@ public static MatrixBlock[] stft(MatrixBlock re, MatrixBlock im, int windowSize, pool.shutdown(); } + + /* + for (int i = 0; i < stftOutput_re.length; i++) { + System.out.println(stftOutput_re[i] + stftOutput_im[i]); + } + + */ + int i = 0; + while (i < 1000000000) { + i = i + 1; + } + i = 0; + while (i < 1000000000) { + i = i + 1; + } + i = 0; + while (i < 1000000000) { + i = i + 1; + } + + + return new MatrixBlock[]{new MatrixBlock(rows, rowLength, stftOutput_re), new MatrixBlock(rows, rowLength, stftOutput_im)}; } @@ -108,5 +132,4 @@ public static MatrixBlock[] stft(MatrixBlock re, MatrixBlock im, int windowSize, public static MatrixBlock[] stft(MatrixBlock re, int windowSize, int overlap, int threads){ return stft(re, new MatrixBlock(re.getNumRows(), re.getNumColumns(), new double[re.getNumRows() * re.getNumColumns()]), windowSize, overlap, threads); } - } diff --git a/src/test/java/org/apache/sysds/test/component/matrix/STFTTest.java b/src/test/java/org/apache/sysds/test/component/matrix/STFTTest.java index 3dcca95da65..cd3437193d5 100644 --- a/src/test/java/org/apache/sysds/test/component/matrix/STFTTest.java +++ b/src/test/java/org/apache/sysds/test/component/matrix/STFTTest.java @@ -42,6 +42,12 @@ public void simple_test() { double[] expected_re = {6, -2, -2, -2, 14, -2, -2, -2, 22, -2, -2, -2, 30, -2, -2, -2, 38, -2, -2, -2, 46, -2, -2, -2, 54, -2, -2, -2}; double[] expected_im = {0, 2, 0, -2, 0, 2, 0, -2, 0, 2, 0, -2, 0, 2, 0, -2, 0, 2, 0, -2, 0, 2, 0, -2, 0, 2, 0, -2}; + /* + for (int i = 0; i < res_re.length; i++) { + System.out.println(res_re[i] + res_im[i]); + } + + */ assertArrayEquals(expected_re, res_re, 0.0001); assertArrayEquals(expected_im, res_im, 0.0001); From 1f8cf4bed056d750b718765dfd5a38a5d2270365 Mon Sep 17 00:00:00 2001 From: Jessica Priebe Date: Mon, 19 Feb 2024 02:25:33 +0100 Subject: [PATCH 129/133] restricted to yes and no --- scripts/keywordSpotting.dml | 4 +-- .../matrix/data/LibMatrixKeywordSpotting.java | 25 +++++++++++++------ 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/scripts/keywordSpotting.dml b/scripts/keywordSpotting.dml index 15bd3589e86..6c46f804586 100644 --- a/scripts/keywordSpotting.dml +++ b/scripts/keywordSpotting.dml @@ -1,7 +1,7 @@ -X = read("tmp/shuffled_features.csv", format="csv") -y = read("tmp/shuffled_labels_minus_and_plus.csv", format="csv") +X = read("tmp/waves.csv", format="csv") +y = read("tmp/labels.csv", format="csv") Xy = cbind(X, y) num_col = ncol(Xy) diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java index a6509dfe7dc..dc17782b948 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java @@ -43,6 +43,8 @@ public class LibMatrixKeywordSpotting { * Please download the * zip file before * running. Save it in "./tmp". + * + * @param args args */ public static void main(String[] args) { @@ -62,9 +64,9 @@ public static void main(String[] args) { private static void saveDataToCSV(String basePath, ZipInputStream zipStream) throws IOException { - PrintWriter commandsCSV = new PrintWriter(new BufferedWriter(new FileWriter(basePath + "commands1.csv"))); - PrintWriter wavesCSV = new PrintWriter(new BufferedWriter(new FileWriter(basePath + "waves1.csv"))); - PrintWriter labelsCSV = new PrintWriter(new BufferedWriter(new FileWriter(basePath + "labels1.csv"))); + PrintWriter commandsCSV = new PrintWriter(new BufferedWriter(new FileWriter(basePath + "commands.csv"))); + PrintWriter wavesCSV = new PrintWriter(new BufferedWriter(new FileWriter(basePath + "waves.csv"))); + PrintWriter labelsCSV = new PrintWriter(new BufferedWriter(new FileWriter(basePath + "labels.csv"))); List commands = new ArrayList<>(); @@ -81,12 +83,14 @@ private static void saveDataToCSV(String basePath, ZipInputStream zipStream) thr String dir = entry.getName(); // remove "/" at the end - String name = dir.substring(mainDir.length(), dir.length() - 1); + String command = dir.substring(mainDir.length(), dir.length() - 1); - commands.add(name); - // save to csv - commandsCSV.print(name); - commandsCSV.println(); + if(command.equals("yes") || command.equals("no")){ + commands.add(command); + // save to csv + commandsCSV.print(command); + commandsCSV.println(); + } } else if(isWavFileToProcess(entry)) { @@ -121,6 +125,11 @@ private static boolean isWavFileToProcess(ZipEntry entry) { if(!path.endsWith(".wav")) return false; + String command = getCommand(entry); + if(!command.equals("yes") && !command.equals("no")){ + return false; + } + int end = path.lastIndexOf('/'); String file = path.substring(end + 1); From b3d52ee5b93948e6e3445ef477a5c079b4044ace Mon Sep 17 00:00:00 2001 From: Jessica Priebe Date: Mon, 19 Feb 2024 02:38:03 +0100 Subject: [PATCH 130/133] addded formatting keyword spotting --- .../sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java index dc17782b948..995dec47df9 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java @@ -85,7 +85,7 @@ private static void saveDataToCSV(String basePath, ZipInputStream zipStream) thr // remove "/" at the end String command = dir.substring(mainDir.length(), dir.length() - 1); - if(command.equals("yes") || command.equals("no")){ + if(command.equals("yes") || command.equals("no")) { commands.add(command); // save to csv commandsCSV.print(command); @@ -99,7 +99,7 @@ else if(isWavFileToProcess(entry)) { AudioFormat format = new AudioFormat(AudioFormat.Encoding.PCM_SIGNED, 16000, 16, 1, 2, 16000, false); int length = (int) Math.ceil((double) entry.getExtra().length / format.getFrameSize()); AudioInputStream audio = new AudioInputStream(new ByteArrayInputStream(entry.getExtra()), format, - length); + length); int[] data = ReaderWavFile.readMonoAudioFromWavFile(audio); // save to csv @@ -126,7 +126,7 @@ private static boolean isWavFileToProcess(ZipEntry entry) { return false; String command = getCommand(entry); - if(!command.equals("yes") && !command.equals("no")){ + if(!command.equals("yes") && !command.equals("no")) { return false; } From 711b766cd9ae14392b90320fd14051d9be087638 Mon Sep 17 00:00:00 2001 From: mufwan Date: Mon, 19 Feb 2024 16:26:30 +0100 Subject: [PATCH 131/133] without parallelization --- scripts/keywordSpotting.dml | 16 ++++++++-------- .../sysds/runtime/matrix/data/LibMatrixSTFT.java | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/scripts/keywordSpotting.dml b/scripts/keywordSpotting.dml index 6c46f804586..1f4bff40792 100644 --- a/scripts/keywordSpotting.dml +++ b/scripts/keywordSpotting.dml @@ -15,8 +15,8 @@ y_shuffled = Xy_shuffled[,num_col] # Labels, the last column [X_train, X_test, y_train, y_test] = split(X=X_shuffled, Y=y_shuffled, seed= 13) # Preprocess with fft Optionally move before split. -[X_train_re, X_train_im] = stft(X_train, 256, 128) -[X_test_re, X_test_im] = stft(X_test, 256, 128) +[X_train_re, X_train_im] = stft(X_train, 4, 2) +[X_test_re, X_test_im] = stft(X_test, 4, 2) X_train_re_sq = X_train_re^2 @@ -38,15 +38,15 @@ magnitudes_test = sqrt(sum_X_test_sq) bias = lm(X=magnitudes_train, y=y_train, reg=1e-1, maxi=10, verbose=TRUE) predictions = lmPredict(X=magnitudes_test, B=bias, verbose=FALSE) -sign_predictions = sign(predictions) -sign_Y = sign(y_test) +#sign_predictions = sign(predictions) +#sign_Y = sign(y_test) -correct = sum(sign_predictions == sign_Y) -total = nrow(y_test) -accuracy = correct / total +#correct = sum(sign_predictions == sign_Y) +#total = nrow(y_test) +#accuracy = correct / total #print(toString(predictions)) -print(toString(accuracy)) +#print(toString(accuracy)) R = lmPredictStats(yhat=predictions, ytest=y_test, lm=TRUE) print(toString(R)) diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixSTFT.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixSTFT.java index 79e8df8eed6..a178154636d 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixSTFT.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixSTFT.java @@ -62,7 +62,7 @@ public static MatrixBlock[] stft(MatrixBlock re, MatrixBlock im, int windowSize, double[] re_inter = new double[out_len]; double[] im_inter = new double[out_len]; - ExecutorService pool = CommonThreadPool.get(1); + //ExecutorService pool = CommonThreadPool.get(1); final ExecutorService pool = CommonThreadPool.get(threads); From 481e42cb68625ccf9f2af4f76ac4b19a77b78d0d Mon Sep 17 00:00:00 2001 From: mufwan Date: Mon, 19 Feb 2024 18:32:21 +0100 Subject: [PATCH 132/133] accuracy for application --- scripts/keywordSpotting.dml | 22 ++-------------------- 1 file changed, 2 insertions(+), 20 deletions(-) diff --git a/scripts/keywordSpotting.dml b/scripts/keywordSpotting.dml index 1f4bff40792..acea8539dc0 100644 --- a/scripts/keywordSpotting.dml +++ b/scripts/keywordSpotting.dml @@ -1,5 +1,4 @@ - X = read("tmp/waves.csv", format="csv") y = read("tmp/labels.csv", format="csv") @@ -11,42 +10,25 @@ Xy_shuffled = Xy_shuffled[,1:num_col] # Remove the random column X_shuffled = Xy_shuffled[,1:(num_col-1)] # Features, excluding the last column which is labels y_shuffled = Xy_shuffled[,num_col] # Labels, the last column - [X_train, X_test, y_train, y_test] = split(X=X_shuffled, Y=y_shuffled, seed= 13) -# Preprocess with fft Optionally move before split. [X_train_re, X_train_im] = stft(X_train, 4, 2) [X_test_re, X_test_im] = stft(X_test, 4, 2) - X_train_re_sq = X_train_re^2 X_train_im_sq = X_train_im^2 - X_test_re_sq = X_test_re^2 X_test_im_sq = X_test_im^2 - -# Sum the squared matrices sum_X_train_sq = X_train_re_sq + X_train_im_sq sum_X_test_sq = X_test_re_sq + X_test_im_sq -# Compute the square root of each element in the sum matrix to get the magnitudes magnitudes_train = sqrt(sum_X_train_sq) magnitudes_test = sqrt(sum_X_test_sq) bias = lm(X=magnitudes_train, y=y_train, reg=1e-1, maxi=10, verbose=TRUE) predictions = lmPredict(X=magnitudes_test, B=bias, verbose=FALSE) -#sign_predictions = sign(predictions) -#sign_Y = sign(y_test) - -#correct = sum(sign_predictions == sign_Y) -#total = nrow(y_test) -#accuracy = correct / total - -#print(toString(predictions)) -#print(toString(accuracy)) - -R = lmPredictStats(yhat=predictions, ytest=y_test, lm=TRUE) -print(toString(R)) +accuracy = auc(Y=y_test, P=predictions) +print(toString(accuracy)) From a1ec350287607bdf5fe4cc6294458ff32e77310e Mon Sep 17 00:00:00 2001 From: mufwan Date: Tue, 20 Feb 2024 21:45:14 +0100 Subject: [PATCH 133/133] new dir for keywordSpotting.dml and fixed bugs from rebase --- .../keywordSpotting}/keywordSpotting.dml | 0 .../parser/BuiltinFunctionExpression.java | 51 +- .../sysds/runtime/io/ReaderWavFile.java | 14 +- .../runtime/matrix/data/LibCommonsMath.java | 24 +- .../matrix/data/LibMatrixKeywordSpotting.java | 2 + .../runtime/matrix/data/LibMatrixSTFT.java | 27 +- .../matrix/FourierTestWithFiles.java | 453 ------------------ .../component/matrix/LibMatrixSTFTTest.java | 104 ---- 8 files changed, 17 insertions(+), 658 deletions(-) rename scripts/{ => tutorials/keywordSpotting}/keywordSpotting.dml (100%) delete mode 100644 src/test/java/org/apache/sysds/test/component/matrix/FourierTestWithFiles.java delete mode 100644 src/test/java/org/apache/sysds/test/component/matrix/LibMatrixSTFTTest.java diff --git a/scripts/keywordSpotting.dml b/scripts/tutorials/keywordSpotting/keywordSpotting.dml similarity index 100% rename from scripts/keywordSpotting.dml rename to scripts/tutorials/keywordSpotting/keywordSpotting.dml diff --git a/src/main/java/org/apache/sysds/parser/BuiltinFunctionExpression.java b/src/main/java/org/apache/sysds/parser/BuiltinFunctionExpression.java index ed3f7ec2005..aef51a2fc12 100644 --- a/src/main/java/org/apache/sysds/parser/BuiltinFunctionExpression.java +++ b/src/main/java/org/apache/sysds/parser/BuiltinFunctionExpression.java @@ -357,7 +357,7 @@ public void validateExpression(MultiAssignmentStatement stmt, HashMap 0) { - raiseValidateError("Missing argument for function " + this.getOpCode(), false, - LanguageErrorCodes.INVALID_PARAMETERS); - } else if (getFifthExpr() != null) { - raiseValidateError("Invalid number of arguments for function " + this.getOpCode().toString().toLowerCase() - + "(). This function only takes 3 or 4 arguments.", false); - } else if (_args.length == 3) { - checkScalarParam(getSecondExpr()); - checkScalarParam(getThirdExpr()); - if (!isPowerOfTwo(((ConstIdentifier) getSecondExpr().getOutput()).getLongValue())) { - raiseValidateError("This FFT implementation is only defined for matrices with dimensions that are powers of 2." + - "The window size (2nd argument) is not a power of two", false, LanguageErrorCodes.INVALID_PARAMETERS); - } - } else if (_args.length == 4) { - checkMatrixParam(getSecondExpr()); - checkScalarParam(getThirdExpr()); - checkScalarParam(getFourthExpr()); - if (!isPowerOfTwo(((ConstIdentifier) getThirdExpr().getOutput()).getLongValue())) { - raiseValidateError("This FFT implementation is only defined for matrices with dimensions that are powers of 2." + - "The window size (3rd argument) is not a power of two", false, LanguageErrorCodes.INVALID_PARAMETERS); - } else if (getFirstExpr().getOutput().getDim2() != getSecondExpr().getOutput().getDim2()) { - raiseValidateError("The real and imaginary part of the provided matrix are of different dimensions.", false); - } else if (getFirstExpr().getOutput().getDim1() != 1 || getSecondExpr().getOutput().getDim1() != 1) { - raiseValidateError("This FFT implementation is only defined for one-dimensional matrices.", false, LanguageErrorCodes.INVALID_PARAMETERS); - } - } - - // setup output properties - DataIdentifier stftOut1 = (DataIdentifier) getOutputs()[0]; - DataIdentifier stftOut2 = (DataIdentifier) getOutputs()[1]; - - // Output1 - stft Values - stftOut1.setDataType(DataType.MATRIX); - stftOut1.setValueType(ValueType.FP64); - stftOut1.setDimensions(getFirstExpr().getOutput().getDim1(), getFirstExpr().getOutput().getDim2()); - stftOut1.setBlocksize(getFirstExpr().getOutput().getBlocksize()); - - // Output2 - stft Vectors - stftOut2.setDataType(DataType.MATRIX); - stftOut2.setValueType(ValueType.FP64); - stftOut2.setDimensions(getFirstExpr().getOutput().getDim1(), getFirstExpr().getOutput().getDim2()); - stftOut2.setBlocksize(getFirstExpr().getOutput().getBlocksize()); - - break; - } case REMOVE: { checkNumParameters(2); checkListParam(getFirstExpr()); diff --git a/src/main/java/org/apache/sysds/runtime/io/ReaderWavFile.java b/src/main/java/org/apache/sysds/runtime/io/ReaderWavFile.java index 25d55fdd474..40f72e838c3 100644 --- a/src/main/java/org/apache/sysds/runtime/io/ReaderWavFile.java +++ b/src/main/java/org/apache/sysds/runtime/io/ReaderWavFile.java @@ -33,7 +33,7 @@ public static int[] readMonoAudioFromWavFile(AudioInputStream audioInputStream) // collapse channels to mono channel int channels = 1; AudioFormat monoAudioFormat = new AudioFormat(audioInputStream.getFormat().getSampleRate(), - audioInputStream.getFormat().getSampleSizeInBits(), channels, true, false); + audioInputStream.getFormat().getSampleSizeInBits(), channels, true, false); AudioInputStream monoAudioInputStream = AudioSystem.getAudioInputStream(monoAudioFormat, audioInputStream); // curation of audio @@ -46,18 +46,18 @@ public static int[] readMonoAudioFromWavFile(AudioInputStream audioInputStream) int bytesRead = audioInputStream.read(audioData); // read operation failed - if(bytesRead == -1) { + if (bytesRead == -1) { return null; } // convert byte array to int array int[] audioValues = new int[numFrames]; - for(int i = 0, frameIndex = 0; i < bytesRead; i += frameSize, frameIndex++) { + for (int i = 0, frameIndex = 0; i < bytesRead; i += frameSize, frameIndex++) { // only use 8 most significant bits int sampleValue = audioData[i + 1] << 8; audioValues[frameIndex] = sampleValue; // only use 8 most significant bits - int sampleValue = audioData[i + 1] << 8; + sampleValue = audioData[i + 1] << 8; audioValues[frameIndex] = sampleValue; } @@ -66,13 +66,9 @@ public static int[] readMonoAudioFromWavFile(AudioInputStream audioInputStream) monoAudioInputStream.close(); return audioValues; - } - catch(IOException e) { - catch(IOException e) { + } catch (IOException e) { e.printStackTrace(); return null; } - } - } diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java index da128c3d8c8..77090b2532a 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibCommonsMath.java @@ -181,7 +181,7 @@ else if (opcode.equals("ifft_linearized")) } public static MatrixBlock[] multiReturnOperations(MatrixBlock in1, MatrixBlock in2, String opcode, int threads, - long seed) { + long seed) { switch (opcode) { case "ifft": @@ -345,18 +345,8 @@ private static MatrixBlock[] computeFFT(MatrixBlock re, int threads) { return new MatrixBlock[]{re, new MatrixBlock(re.getNumRows(), re.getNumColumns(), true)}; // Assuming you need to return two matrices: the real part and an imaginary part initialized to 0. } // run fft - in.sparseToDense(); - return fft(in); - } - - private static boolean isMatrixAllZeros(MatrixBlock matrix) { - // Fast check for sparse representation - if (matrix.isInSparseFormat()) { - return matrix.getNonZeros() == 0; - } - // Dense format check - double sum = matrix.sum(); - return sum == 0; + re.sparseToDense(); + return fft(re, threads); } /** @@ -443,10 +433,10 @@ private static MatrixBlock[] computeIFFT_LINEARIZED(MatrixBlock re, MatrixBlock return ifft_linearized(re, threads); } } - + /** * Function to perform IFFT_LINEARIZED on a given matrix - * + * * @param re matrix object * @param threads number of threads * @return array of matrix blocks @@ -454,7 +444,7 @@ private static MatrixBlock[] computeIFFT_LINEARIZED(MatrixBlock re, MatrixBlock private static MatrixBlock[] computeIFFT_LINEARIZED(MatrixBlock re, int threads){ return computeIFFT_LINEARIZED(re, null, threads); } - + /** * Function to perform STFT on a given matrix. * @@ -489,7 +479,7 @@ private static MatrixBlock[] computeSTFT(MatrixBlock re, MatrixBlock im, int win double[] out_zero = new double[out_len]; return new MatrixBlock[]{new MatrixBlock(rows, rowLength, out_zero), new MatrixBlock(rows, rowLength, out_zero)}; - } + } re.sparseToDense(); return stft(re, windowSize, overlap, threads); } diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java index 995dec47df9..195e3625b30 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixKeywordSpotting.java @@ -36,6 +36,8 @@ import java.util.Arrays; import java.util.Arrays; import java.util.ArrayList; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; public class LibMatrixKeywordSpotting { diff --git a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixSTFT.java b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixSTFT.java index a178154636d..878dda9ab5e 100644 --- a/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixSTFT.java +++ b/src/main/java/org/apache/sysds/runtime/matrix/data/LibMatrixSTFT.java @@ -62,8 +62,6 @@ public static MatrixBlock[] stft(MatrixBlock re, MatrixBlock im, int windowSize, double[] re_inter = new double[out_len]; double[] im_inter = new double[out_len]; - //ExecutorService pool = CommonThreadPool.get(1); - final ExecutorService pool = CommonThreadPool.get(threads); final List> tasks = new ArrayList<>(); @@ -93,28 +91,6 @@ public static MatrixBlock[] stft(MatrixBlock re, MatrixBlock im, int windowSize, pool.shutdown(); } - - /* - for (int i = 0; i < stftOutput_re.length; i++) { - System.out.println(stftOutput_re[i] + stftOutput_im[i]); - } - - */ - int i = 0; - while (i < 1000000000) { - i = i + 1; - } - i = 0; - while (i < 1000000000) { - i = i + 1; - } - i = 0; - while (i < 1000000000) { - i = i + 1; - } - - - return new MatrixBlock[]{new MatrixBlock(rows, rowLength, stftOutput_re), new MatrixBlock(rows, rowLength, stftOutput_im)}; } @@ -132,4 +108,5 @@ public static MatrixBlock[] stft(MatrixBlock re, MatrixBlock im, int windowSize, public static MatrixBlock[] stft(MatrixBlock re, int windowSize, int overlap, int threads){ return stft(re, new MatrixBlock(re.getNumRows(), re.getNumColumns(), new double[re.getNumRows() * re.getNumColumns()]), windowSize, overlap, threads); } -} + +} \ No newline at end of file diff --git a/src/test/java/org/apache/sysds/test/component/matrix/FourierTestWithFiles.java b/src/test/java/org/apache/sysds/test/component/matrix/FourierTestWithFiles.java deleted file mode 100644 index fe051e2c272..00000000000 --- a/src/test/java/org/apache/sysds/test/component/matrix/FourierTestWithFiles.java +++ /dev/null @@ -1,453 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package org.apache.sysds.test.component.matrix; - -import org.junit.Test; -import java.io.IOException; -import java.io.BufferedReader; -import java.io.FileReader; - -import static org.junit.Assert.assertTrue; -import static org.apache.sysds.runtime.matrix.data.LibMatrixFourier.fft; -import static org.apache.sysds.runtime.matrix.data.LibMatrixFourier.ifft; - -public class FourierTestWithFiles { - int progressInterval = 5000; - - // prior to executing the following tests it is necessary to run the Numpy Script in FourierTestData.py - // and add the generated files to the root of the project. - @Test - public void testFftWithNumpyData() throws IOException { - - String filename = "fft_data.csv"; // Path to your CSV file - String path = "./src/test/java/org/apache/sysds/test/component/matrix/"; - - BufferedReader reader = new BufferedReader(new FileReader(path+filename)); - String line; - int lineNumber = 0; - long totalTime = 0; // Total time for all FFT computations - int numCalculations = 0; // Number of FFT computations - - while ((line = reader.readLine()) != null) { - lineNumber++; - - String[] values = line.split(","); - int n = values.length / 3; - - double[] re = new double[n]; - double[] im = new double[n]; - - double[][] expected = new double[2][n]; // First row for real, second row for imaginary parts - - for (int i = 0; i < n; i++) { - re[i] = Double.parseDouble(values[i]); - expected[0][i] = Double.parseDouble(values[n + i]); // Real part - expected[1][i] = Double.parseDouble(values[n * 2 + i]); // Imaginary part - } - - long startTime = System.nanoTime(); - fft(re, im, 1, n); - long endTime = System.nanoTime(); - - if(lineNumber > 1000){ - totalTime += (endTime - startTime); - numCalculations++; - - if (numCalculations % progressInterval == 0) { - double averageTime = (totalTime / 1e6) / numCalculations; // Average time in milliseconds - System.out.println("fft(double[][][] in): Average execution time after " + numCalculations + " calculations: " + String.format("%.8f", averageTime/1000) + " s"); - } - } - - double[][] actual = {re, im}; - - // Validate the FFT results - validateFftResults(expected, actual, lineNumber); - } - - reader.close(); - } - - private void validateFftResults(double[][] expected, double[][] actual, int lineNumber) { - - int length = expected[0].length; - - for (int i = 0; i < length; i++) { - double realActual = actual[0][i]; - double imagActual = actual[1][i]; - assertEquals("Mismatch in real part at index " + i + " in line " + lineNumber, expected[0][i], realActual, 1e-9); - assertEquals("Mismatch in imaginary part at index " + i + " in line " + lineNumber, expected[1][i], imagActual, 1e-9); - } - - if(lineNumber % progressInterval == 0){ - System.out.println("fft(double[][][] in): Finished processing line " + lineNumber); - } - - } - - @Test - public void testFftExecutionTime() throws IOException { - - String filename = "fft_data.csv"; // Path to your CSV file - String path = "./src/test/java/org/apache/sysds/test/component/matrix/"; - - BufferedReader reader = new BufferedReader(new FileReader(path+filename)); - String line; - int lineNumber = 0; - long totalTime = 0; // Total time for all FFT computations - int numCalculations = 0; // Number of FFT computations - - while ((line = reader.readLine()) != null) { - - lineNumber++; - String[] values = line.split(","); - int n = values.length / 3; - - double[] re = new double[n]; - double[] im = new double[n]; - - for (int i = 0; i < n; i++) { - re[i] = Double.parseDouble(values[i]); // Real part - im[i] = Double.parseDouble(values[n + i]); // Imaginary part - } - - long startTime = System.nanoTime(); - - fft(re, im, 1, n); - - long endTime = System.nanoTime(); - - if(lineNumber > 1000){ - totalTime += (endTime - startTime); - numCalculations++; - - if (numCalculations % progressInterval == 0) { - double averageTime = (totalTime / 1e6) / numCalculations; // Average time in milliseconds - System.out.println("fft_old(double[][][] in, boolean calcInv) Average execution time after " + numCalculations + " calculations: " + String.format("%.8f", averageTime/1000) + " s"); - } - } - } - - reader.close(); - } - - @Test - public void testFftExecutionTimeOfOneDimFFT() throws IOException { - - String filename = "fft_data.csv"; // Path to your CSV file - String path = "./src/test/java/org/apache/sysds/test/component/matrix/"; - - BufferedReader reader = new BufferedReader(new FileReader(path+filename)); - String line; - int lineNumber = 0; - long totalTime = 0; // Total time for all FFT computations - int numCalculations = 0; // Number of FFT computations - - while ((line = reader.readLine()) != null) { - - lineNumber++; - String[] values = line.split(","); - int n = values.length / 2; - - double[] re = new double[n]; - double[] im = new double[n]; - - for (int i = 0; i < n; i++) { - re[i] = Double.parseDouble(values[i]); // Real part - im[i] = Double.parseDouble(values[n + i]); // Imaginary part - } - - long startTime = System.nanoTime(); - // one dimensional - fft(re, im, 1, n); - - long endTime = System.nanoTime(); - - if(lineNumber > 1000){ - totalTime += (endTime - startTime); - numCalculations++; - - if (numCalculations % progressInterval == 0) { - double averageTime = (totalTime / 1e6) / numCalculations; // Average time in milliseconds - System.out.println("fft_one_dim: Average execution time after " + numCalculations + " calculations: " + String.format("%.8f", averageTime/1000) + " s "); - } - } - } - - reader.close(); - } - - // prior to executing this test it is necessary to run the Numpy Script in FourierTestData.py and add the generated file to the root of the project. - @Test - public void testIfftWithRealNumpyData() throws IOException { - - String filename = "ifft_data.csv"; // Path to your CSV file - String path = "./src/test/java/org/apache/sysds/test/component/matrix/"; - - BufferedReader reader = new BufferedReader(new FileReader(path+filename)); - String line; - int lineNumber = 0; - - while ((line = reader.readLine()) != null) { - - lineNumber++; - String[] values = line.split(","); - int n = values.length / 3; - - double[] re = new double[n]; - double[] im = new double[n]; - - double[][] expected = new double[2][n]; // First row for real, second row for imaginary parts - - for (int i = 0; i < n; i++) { - re[i] = Double.parseDouble(values[i]); // Real part of input - // Imaginary part of input is assumed to be 0 - expected[0][i] = Double.parseDouble(values[n + i]); // Real part of expected output - expected[1][i] = Double.parseDouble(values[n * 2 + i]); // Imaginary part of expected output - } - - ifft(re, im, 1, n); // Perform IFFT - - double[][] actual = new double[][]{re, im}; - // Validate the IFFT results - validateFftResults(expected, actual, lineNumber); - } - - reader.close(); - - } - - @Test - public void testIfftWithComplexNumpyData() throws IOException { - - String filename = "complex_ifft_data.csv"; // Adjusted path to your IFFT data file with complex inputs - String path = "./src/test/java/org/apache/sysds/test/component/matrix/"; - - BufferedReader reader = new BufferedReader(new FileReader(path+filename)); - String line; - int lineNumber = 0; - long totalTime = 0; // Total time for all IFFT computations - int numCalculations = 0; // Number of IFFT computations - - while ((line = reader.readLine()) != null) { - lineNumber++; - String[] values = line.split(","); - int n = values.length / 4; // Adjusted for complex numbers - - // Real and imaginary parts - double[] re = new double[n]; - double[] im = new double[n]; - - double[][] expected = new double[2][n]; // Expected real and imaginary parts - - for (int i = 0; i < n; i++) { - re[i] = Double.parseDouble(values[i]); // Real part of input - im[i] = Double.parseDouble(values[i + n]); // Imaginary part of input - expected[0][i] = Double.parseDouble(values[i + 2 * n]); // Expected real part - expected[1][i] = Double.parseDouble(values[i + 3 * n]); // Expected imaginary part - } - - long startTime = System.nanoTime(); - - ifft(re, im, 1, n); // Perform IFFT - - long endTime = System.nanoTime(); - - if (lineNumber > 1000) { - totalTime += (endTime - startTime); - numCalculations++; - } - - double[][] actual = new double[][]{re, im}; - // Validate the IFFT results - validateComplexIFftResults(expected, actual, lineNumber); - - if (lineNumber % progressInterval == 0) { - double averageTime = (totalTime / 1e6) / numCalculations; // Average time in milliseconds - System.out.println("ifft: Average execution time after " + numCalculations + " calculations: " + String.format("%.8f", averageTime / 1000) + " s"); - } - } - - reader.close(); - } - - private void validateComplexIFftResults(double[][] expected, double[][] actual, int lineNumber) { - - int length = expected[0].length; - - for (int i = 0; i < length; i++) { - double realActual = actual[0][i]; - double imagActual = actual[1][i]; - assertEquals("Mismatch in real part at index " + i + " in line " + lineNumber, expected[0][i], realActual, 1e-9); - assertEquals("Mismatch in imaginary part at index " + i + " in line " + lineNumber, expected[1][i], imagActual, 1e-9); - } - - if (lineNumber % progressInterval == 0) { - System.out.println("ifft(complex input): Finished processing line " + lineNumber); - } - - } - - @Test - public void testFft2dWithNumpyData() throws IOException { - - String filename = "complex_fft_2d_data.csv"; // path to your 2D FFT data file - String path = "./src/test/java/org/apache/sysds/test/component/matrix/"; - - BufferedReader reader = new BufferedReader(new FileReader(path+filename)); - String line; - int lineNumber = 0; - long totalTime = 0; // Total time for all FFT 2D computations - int numCalculations = 0; // Number of FFT 2D computations - - while ((line = reader.readLine()) != null) { - lineNumber++; - String[] values = line.split(","); - int halfLength = values.length / 4; - int sideLength = (int) Math.sqrt(halfLength); // Assuming square matrix - - // Real and imaginary parts - double[] re = new double[sideLength*sideLength]; - double[] im = new double[sideLength*sideLength]; - - double[][][] expected = new double[2][sideLength][sideLength]; - - for (int i = 0; i < halfLength; i++) { - int row = i / sideLength; - int col = i % sideLength; - - // i == row*sideLength+col?! - re[row*sideLength+col] = Double.parseDouble(values[i]); - im[row*sideLength+col] = Double.parseDouble(values[i + halfLength]); - expected[0][row][col] = Double.parseDouble(values[i + 2 * halfLength]); - expected[1][row][col] = Double.parseDouble(values[i + 3 * halfLength]); - } - - long startTime = System.nanoTime(); - // Use your fft2d implementation - fft(re, im, sideLength, sideLength); - //double[][][] javaFftResult = fft_old(input, false); // Use your fft2d implementation - long endTime = System.nanoTime(); - totalTime += (endTime - startTime); - numCalculations++; - - for (int i = 0; i < sideLength; i++) { - for (int j = 0; j < sideLength; j++) { - assertComplexEquals("Mismatch at [" + i + "][" + j + "] in line " + lineNumber, - expected[0][i][j], expected[1][i][j], - re[i*sideLength+j], im[i*sideLength+j]); - } - } - - if (lineNumber % progressInterval == 0) { - double averageTime = (totalTime / 1e6) / numCalculations; // Average time in milliseconds - System.out.println("fft2d: Average execution time after " + numCalculations + " calculations: " + String.format("%.8f", averageTime/1000) + " s"); - } - } - - reader.close(); - System.out.println("fft2d: Finished processing " + lineNumber + " lines.\n Average execution time: " + String.format("%.8f", (totalTime / 1e6 / numCalculations)/1000) + " s"); - - } - - - @Test - public void testIfft2dWithNumpyData() throws IOException { - - String filename = "complex_ifft_2d_data.csv"; // path to your 2D IFFT data file - String path = "./src/test/java/org/apache/sysds/test/component/matrix/"; - - BufferedReader reader = new BufferedReader(new FileReader(path+filename)); - String line; - int lineNumber = 0; - long totalTime = 0; // Total time for all IFFT 2D computations - int numCalculations = 0; // Number of IFFT 2D computations - - while ((line = reader.readLine()) != null) { - - lineNumber++; - String[] values = line.split(","); - int halfLength = values.length / 4; - int sideLength = (int) Math.sqrt(halfLength); // Assuming square matrix - - // Real and imaginary parts - double[] re = new double[sideLength*sideLength]; - double[] im = new double[sideLength*sideLength]; - - double[][][] expected = new double[2][sideLength][sideLength]; - - for (int i = 0; i < halfLength; i++) { - int row = i / sideLength; - int col = i % sideLength; - - re[row*sideLength+col] = Double.parseDouble(values[i]); - im[row*sideLength+col] = Double.parseDouble(values[i + halfLength]); - - expected[0][row][col] = Double.parseDouble(values[i + 2 * halfLength]); - expected[1][row][col] = Double.parseDouble(values[i + 3 * halfLength]); - } - - long startTime = System.nanoTime(); - - // Use your ifft2d implementation - ifft(re, im, sideLength, sideLength); - - long endTime = System.nanoTime(); - if(lineNumber > 1000){ - totalTime += (endTime - startTime); - numCalculations++; - } - - for (int i = 0; i < sideLength; i++) { - for (int j = 0; j < sideLength; j++) { - assertComplexEquals("Mismatch at [" + i + "][" + j + "] in line " + lineNumber, - expected[0][i][j], expected[1][i][j], - re[i*sideLength+j], im[i*sideLength+j]); - } - } - - if (lineNumber % progressInterval == 0) { - System.out.println("fft2d/ifft2d: Finished processing line " + lineNumber); - double averageTime = (totalTime / 1e6) / numCalculations; // Average time in milliseconds - System.out.println("Ifft2d Average execution time after " + numCalculations + " calculations: " + String.format("%.8f", averageTime/1000) + " s"); - } - } - - reader.close(); - System.out.println("ifft2d: Finished processing " + lineNumber + " lines.\n Average execution time: " + String.format("%.8f", (totalTime / 1e6 / numCalculations)/1000) + " ms"); - - } - - // Helper method for asserting equality with a tolerance - private static void assertEquals(String message, double expected, double actual, double tolerance) { - assertTrue(message + " - Expected: " + expected + ", Actual: " + actual, Math.abs(expected - actual) <= tolerance); - } - - private void assertComplexEquals(String message, double expectedReal, double expectedImag, double actualReal, double actualImag) { - - final double EPSILON = 1e-9; - assertTrue(message + " - Mismatch in real part. Expected: " + expectedReal + ", Actual: " + actualReal, - Math.abs(expectedReal - actualReal) <= EPSILON); - assertTrue(message + " - Mismatch in imaginary part. Expected: " + expectedImag + ", Actual: " + actualImag, - Math.abs(expectedImag - actualImag) <= EPSILON); - - } - -} diff --git a/src/test/java/org/apache/sysds/test/component/matrix/LibMatrixSTFTTest.java b/src/test/java/org/apache/sysds/test/component/matrix/LibMatrixSTFTTest.java deleted file mode 100644 index e2ed4a622c0..00000000000 --- a/src/test/java/org/apache/sysds/test/component/matrix/LibMatrixSTFTTest.java +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one - * or more contributor license agreements. See the NOTICE file - * distributed with this work for additional information - * regarding copyright ownership. The ASF licenses this file - * to you under the Apache License, Version 2.0 (the - * "License"); you may not use this file except in compliance - * with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, - * software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY - * KIND, either express or implied. See the License for the - * specific language governing permissions and limitations - * under the License. - */ - -package org.apache.sysds.test.component.matrix; - -import org.apache.sysds.runtime.matrix.data.MatrixBlock; -import org.junit.Test; - -import static org.junit.Assert.assertArrayEquals; -import static org.apache.sysds.runtime.matrix.data.LibMatrixSTFT.stft; -import static org.apache.sysds.runtime.matrix.data.LibMatrixSTFT.one_dim_stft; - -public class LibMatrixSTFTTest { - - @Test - public void simple_test() { - - double[] signal = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}; - int windowSize = 4; - int overlap = 2; - double[][] stftResult = one_dim_stft(signal, windowSize, overlap); - - // 1st row real part, 2nd row imaginary part - double[][] expected = {{6, -2, -2, -2, 14, -2, -2, -2, 22, -2, -2, -2, 30, -2, -2, -2, 38, -2, -2, -2, 46, -2, -2, -2, 54, -2, -2, -2},{0, 2, 0, -2, 0, 2, 0, -2, 0, 2, 0, -2, 0, 2, 0, -2, 0, 2, 0, -2, 0, 2, 0, -2, 0, 2, 0, -2}}; - - for(double[] row : stftResult){ - for (double elem : row){ - System.out.print(elem + " "); - } - System.out.println(); - } - - assertArrayEquals(expected[0], stftResult[0], 0.0001); - assertArrayEquals(expected[1], stftResult[1], 0.0001); - - } - - @Test - public void matrix_block_one_dim_test(){ - - double[] in = {0, 18, -15, 3}; - - double[] expected_re = {6,15,-36,15}; - double[] expected_im = {0,-15,0,15}; - - MatrixBlock[] res = stft(in, 4, 0); - double[] res_re = res[0].getDenseBlockValues(); - double[] res_im = res[1].getDenseBlockValues(); - - for(double elem : res_re){ - System.out.print(elem+" "); - } - System.out.println(); - for(double elem : res_im){ - System.out.print(elem+" "); - } - - assertArrayEquals(expected_re, res_re, 0.0001); - assertArrayEquals(expected_im, res_im, 0.0001); - - } - - @Test - public void matrix_block_one_dim_test2(){ - - double[] in = {10, 5, -3, 8, 15, -6, 2, 0}; - - double[] expected_re = {20.0, 13.0, -6.0, 13.0, 14.0, -18.0, 10.0, -18.0, 11.0, 13.0, 23.0, 13.0}; - double[] expected_im = {0.0, 3.0, 0.0, -3.0, 0.0, -14.0, 0.0, 14.0, 0.0, 6.0, 0.0, -6.0 }; - - MatrixBlock[] res = stft(in, 4, 2); - double[] res_re = res[0].getDenseBlockValues(); - double[] res_im = res[1].getDenseBlockValues(); - - for(double elem : res_re){ - System.out.print(elem+" "); - } - System.out.println(); - for(double elem : res_im){ - System.out.print(elem+" "); - } - - assertArrayEquals(expected_re, res_re, 0.0001); - assertArrayEquals(expected_im, res_im, 0.0001); - - } - -}