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(); }