From d3997561fcc9e40676432e16e66a4403573dc9b3 Mon Sep 17 00:00:00 2001 From: "J. Neugebauer" Date: Tue, 19 Jul 2022 22:53:46 +0200 Subject: [PATCH] Streams durch Schleifen ersetzt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Der Overhead durch die parallelen Streams war zu hoch. Jedenfalls bei den relativ kleinen Matrizen im Test. Bei größeren Matrizen könnte die Parallelität einen Vorteil bringen. Ggf. sollte dies getesett werden und abhängig von der Größe die bestte Methode gewählt werden. --- .../java/schule/ngb/zm/ml/DoubleMatrix.java | 202 ++++++++++++------ 1 file changed, 137 insertions(+), 65 deletions(-) diff --git a/src/main/java/schule/ngb/zm/ml/DoubleMatrix.java b/src/main/java/schule/ngb/zm/ml/DoubleMatrix.java index 758a929..4a4008c 100644 --- a/src/main/java/schule/ngb/zm/ml/DoubleMatrix.java +++ b/src/main/java/schule/ngb/zm/ml/DoubleMatrix.java @@ -2,9 +2,7 @@ package schule.ngb.zm.ml; import schule.ngb.zm.Constants; -import java.util.Arrays; import java.util.function.DoubleUnaryOperator; -import java.util.stream.IntStream; // TODO: Move Math into Matrix class // TODO: Implement support for optional sci libs @@ -12,20 +10,33 @@ public final class DoubleMatrix implements MLMatrix { private int columns, rows; - double[][] coefficients; + double[] coefficients; - public DoubleMatrix( int rows, int cols ) { + public DoubleMatrix( int rows, int cols ) { this.rows = rows; this.columns = cols; - coefficients = new double[rows][cols]; + coefficients = new double[rows * cols]; } public DoubleMatrix( double[][] coefficients ) { this.rows = coefficients.length; this.columns = coefficients[0].length; - this.coefficients = Arrays.stream(coefficients) - .map(double[]::clone) - .toArray(double[][]::new); + this.coefficients = new double[rows * columns]; + for( int j = 0; j < columns; j++ ) { + for( int i = 0; i < rows; i++ ) { + this.coefficients[idx(i, j)] = coefficients[i][j]; + } + } + } + + public DoubleMatrix( DoubleMatrix other ) { + this.rows = other.rows(); + this.columns = other.columns(); + this.coefficients = new double[rows * columns]; + System.arraycopy( + other.coefficients, 0, + this.coefficients, 0, + rows * columns); } public int columns() { @@ -37,15 +48,19 @@ public final class DoubleMatrix implements MLMatrix { } public double[][] coefficients() { - return coefficients; + return new double[rows][columns]; + } + + int idx( int r, int c ) { + return c * rows + r; } public double get( int row, int col ) { - return coefficients[row][col]; + return coefficients[idx(row, col)]; } public MLMatrix set( int row, int col, double value ) { - coefficients[row][col] = value; + coefficients[idx(row, col)] = value; return this; } @@ -54,145 +69,202 @@ public final class DoubleMatrix implements MLMatrix { } public MLMatrix initializeRandom( double lower, double upper ) { - applyInPlace((d) -> ((upper-lower) * Constants.random()) + lower); + applyInPlace(( d ) -> ((upper - lower) * Constants.random()) + lower); return this; } public MLMatrix initializeOne() { - applyInPlace((d) -> 1.0); + applyInPlace(( d ) -> 1.0); return this; } public MLMatrix initializeZero() { - applyInPlace((d) -> 0.0); + applyInPlace(( d ) -> 0.0); return this; } @Override public MLMatrix duplicate() { - return new DoubleMatrix(coefficients); + return new DoubleMatrix(this); } @Override public MLMatrix multiplyTransposed( MLMatrix B ) { - return new DoubleMatrix(IntStream.range(0, rows).parallel().mapToObj( + /*return new DoubleMatrix(IntStream.range(0, rows).parallel().mapToObj( ( i ) -> IntStream.range(0, B.rows()).mapToDouble( - ( j ) -> IntStream.range(0, columns).mapToDouble( - (k) -> coefficients[i][k]*B.get(j,k) + ( j ) -> IntStream.range(0, columns).mapToDouble( + ( k ) -> get(i, k) * B.get(j, k) ).sum() ).toArray() - ).toArray(double[][]::new)); + ).toArray(double[][]::new));*/ + DoubleMatrix result = new DoubleMatrix(rows, B.rows()); + for( int i = 0; i < rows; i++ ) { + for( int j = 0; j < B.rows(); j++ ) { + result.coefficients[result.idx(i, j)] = 0.0; + for( int k = 0; k < columns; k++ ) { + result.coefficients[result.idx(i, j)] += get(i, k) * B.get(j, k); + } + } + } + return result; } @Override public MLMatrix multiplyAddBias( final MLMatrix B, final MLMatrix C ) { - return new DoubleMatrix(IntStream.range(0, rows).parallel().mapToObj( + /*return new DoubleMatrix(IntStream.range(0, rows).parallel().mapToObj( ( i ) -> IntStream.range(0, B.columns()).mapToDouble( - ( j ) -> IntStream.range(0, columns).mapToDouble( - (k) -> coefficients[i][k]*B.get(k,j) + ( j ) -> IntStream.range(0, columns).mapToDouble( + ( k ) -> get(i, k) * B.get(k, j) ).sum() + C.get(0, j) ).toArray() - ).toArray(double[][]::new)); + ).toArray(double[][]::new));*/ + DoubleMatrix result = new DoubleMatrix(rows, B.columns()); + for( int i = 0; i < rows; i++ ) { + for( int j = 0; j < B.columns(); j++ ) { + result.coefficients[result.idx(i, j)] = 0.0; + for( int k = 0; k < columns; k++ ) { + result.coefficients[result.idx(i, j)] += get(i, k) * B.get(k, j); + } + result.coefficients[result.idx(i, j)] += C.get(0, j); + } + } + return result; } @Override public MLMatrix transposedMultiplyAndScale( final MLMatrix B, final double scalar ) { - return new DoubleMatrix(IntStream.range(0, columns).parallel().mapToObj( + /*return new DoubleMatrix(IntStream.range(0, columns).parallel().mapToObj( ( i ) -> IntStream.range(0, B.columns()).mapToDouble( ( j ) -> IntStream.range(0, rows).mapToDouble( - (k) -> coefficients[k][i]*B.get(k,j)*scalar + ( k ) -> get(k, i) * B.get(k, j) * scalar ).sum() ).toArray() - ).toArray(double[][]::new)); + ).toArray(double[][]::new));*/ + DoubleMatrix result = new DoubleMatrix(columns, B.columns()); + for( int i = 0; i < columns; i++ ) { + for( int j = 0; j < B.columns(); j++ ) { + result.coefficients[result.idx(i, j)] = 0.0; + for( int k = 0; k < rows; k++ ) { + result.coefficients[result.idx(i, j)] += get(k, i) * B.get(k, j); + } + result.coefficients[result.idx(i, j)] *= scalar; + } + } + return result; } @Override public MLMatrix add( MLMatrix B ) { - return new DoubleMatrix(IntStream.range(0, rows).parallel().mapToObj( + /*return new DoubleMatrix(IntStream.range(0, rows).parallel().mapToObj( ( i ) -> IntStream.range(0, columns).mapToDouble( - ( j ) -> coefficients[i][j] + B.get(i, j) + ( j ) -> get(i, j) + B.get(i, j) ).toArray() - ).toArray(double[][]::new)); + ).toArray(double[][]::new));*/ + DoubleMatrix sum = new DoubleMatrix(rows, columns); + for( int j = 0; j < columns; j++ ) { + for( int i = 0; i < rows; i++ ) { + sum.coefficients[idx(i, j)] = coefficients[idx(i, j)] + B.get(i, j); + } + } + return sum; } @Override public MLMatrix addInPlace( MLMatrix B ) { - coefficients = IntStream.range(0, rows).parallel().mapToObj( - ( i ) -> IntStream.range(0, columns).mapToDouble( - ( j ) -> coefficients[i][j] + B.get(i, j) - ).toArray() - ).toArray(double[][]::new); + for( int j = 0; j < columns; j++ ) { + for( int i = 0; i < rows; i++ ) { + coefficients[idx(i, j)] += B.get(i, j); + } + } return this; } @Override public MLMatrix sub( MLMatrix B ) { - return new DoubleMatrix(IntStream.range(0, rows).parallel().mapToObj( + /*return new DoubleMatrix(IntStream.range(0, rows).parallel().mapToObj( ( i ) -> IntStream.range(0, columns).mapToDouble( - ( j ) -> coefficients[i][j] - B.get(i, j) + ( j ) -> get(i, j) - B.get(i, j) ).toArray() - ).toArray(double[][]::new)); + ).toArray(double[][]::new));*/ + DoubleMatrix diff = new DoubleMatrix(rows, columns); + for( int j = 0; j < columns; j++ ) { + for( int i = 0; i < rows; i++ ) { + diff.coefficients[idx(i, j)] = coefficients[idx(i, j)] - B.get(i, j); + } + } + return diff; } @Override public MLMatrix colSums() { - double[][] sums = new double[1][columns]; - for( int c = 0; c < columns; c++ ) { - for( int r = 0; r < rows; r++ ) { - sums[0][c] += coefficients[r][c]; + /*DoubleMatrix colSums = new DoubleMatrix(1, columns); + colSums.coefficients = IntStream.range(0, columns).parallel().mapToDouble( + ( j ) -> IntStream.range(0, rows).mapToDouble( + ( i ) -> get(i, j) + ).sum() + ).toArray(); + return colSums;*/ + DoubleMatrix colSums = new DoubleMatrix(1, columns); + for( int j = 0; j < columns; j++ ) { + colSums.coefficients[j] = 0.0; + for( int i = 0; i < rows; i++ ) { + colSums.coefficients[j] += coefficients[idx(i, j)]; } } - return new DoubleMatrix(sums); + return colSums; } @Override public MLMatrix scaleInPlace( final double scalar ) { - coefficients = Arrays.stream(coefficients).parallel().map( - ( arr ) -> Arrays.stream(arr).map( - (d) -> d * scalar - ).toArray() - ).toArray(double[][]::new); + for( int i = 0; i < coefficients.length; i++ ) { + coefficients[i] *= scalar; + } return this; } @Override public MLMatrix scaleInPlace( final MLMatrix S ) { - coefficients = IntStream.range(0, coefficients.length).parallel().mapToObj( - ( i ) -> IntStream.range(0, coefficients[i].length).mapToDouble( - ( j ) -> coefficients[i][j] * S.get(i, j) - ).toArray() - ).toArray(double[][]::new); + for( int j = 0; j < columns; j++ ) { + for( int i = 0; i < rows; i++ ) { + coefficients[idx(i, j)] *= S.get(i, j); + } + } return this; } @Override public MLMatrix apply( DoubleUnaryOperator op ) { - return new DoubleMatrix(Arrays.stream(coefficients).parallel().map( - ( arr ) -> Arrays.stream(arr).map(op).toArray() - ).toArray(double[][]::new)); + DoubleMatrix result = new DoubleMatrix(rows, columns); + for( int i = 0; i < coefficients.length; i++ ) { + result.coefficients[i] = op.applyAsDouble(coefficients[i]); + } + return result; } @Override public MLMatrix applyInPlace( DoubleUnaryOperator op ) { - this.coefficients = Arrays.stream(coefficients).parallel().map( - ( arr ) -> Arrays.stream(arr).map(op).toArray() - ).toArray(double[][]::new); + for( int i = 0; i < coefficients.length; i++ ) { + coefficients[i] = op.applyAsDouble(coefficients[i]); + } return this; } @Override public String toString() { - //return Arrays.deepToString(coefficients); StringBuilder sb = new StringBuilder(); - sb.append('['); + sb.append(rows); + sb.append(" x "); + sb.append(columns); + sb.append(" Matrix"); sb.append('\n'); - for( int i = 0; i < coefficients.length; i++ ) { - sb.append('\t'); - sb.append(Arrays.toString(coefficients[i])); + for( int i = 0; i < rows; i++ ) { + for( int j = 0; j < columns; j++ ) { + sb.append(get(i, j)); + if( j < columns - 1 ) + sb.append(' '); + } sb.append('\n'); } - sb.append(']'); - return sb.toString(); }