Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to multiply 2 double[][] matrices using streams?

I'm wondering what the most compact and efficient way to multiple 2 double[][] arrays matrices using streams. The approach should follow matrix multiplication rules as illustrated here:

Matrix Multiplication: How to Multiply Two Matrices Together

Here's one way to do it using for loops (this is the first matrix):

final int nRows = this.getRowDimension();
final int nCols = m.getColumnDimension();
final int nSum = this.getColumnDimension();

final double[][] outData = new double[nRows][nCols];
// Will hold a column of "m".
final double[] mCol = new double[nSum];
final double[][] mData = m.data;

// Multiply.
for (int col = 0; col < nCols; col++) {
    // Copy all elements of column "col" of "m" so that
    // will be in contiguous memory.
    for (int mRow = 0; mRow < nSum; mRow++) {
        mCol[mRow] = mData[mRow][col];
    }

    for (int row = 0; row < nRows; row++) {
        final double[] dataRow = data[row];
        double sum = 0;
        for (int i = 0; i < nSum; i++) {
            sum += dataRow[i] * mCol[i];
        }
        outData[row][col] = sum;
    }
}

The procedure should fit the following test data:

double[][] md1 = {{4d, 8d}, {0d, 2d}, {1d, 6d}};
double[][] md2 = {{5d, 2d, 5d, 5d}, {9d, 4d, 5d, 5d}};

double[][] mb1 = {{4d, 8d}, {0d, 2d}, {1d, 6d}};
double[][] mb2 = {{5d}, {9d}};
like image 274
Ole Avatar asked Dec 05 '25 23:12

Ole


2 Answers

A more compact and readable solution is to create a Stream over the rows of the first matrix, map each row to the result of multiplying it with the second matrix column and collect that back into a double[][].

public static void main(String[] args) {
    double[][] m1 = {{4, 8}, {0, 2}, {1, 6}};
    double[][] m2 = {{5, 2}, {9, 4}};

    double[][] result = Arrays.stream(m1)
            .map(r -> IntStream.range(0, m2[0].length)
                    .mapToDouble(i -> IntStream.range(0, m2.length)
                            .mapToDouble(j -> r[j] * m2[j][i]).sum())
                    .toArray())
            .toArray(double[][]::new);

    System.out.println(Arrays.deepToString(result));
    // [[92.0, 40.0], [18.0, 8.0], [59.0, 26.0]]
}

This will calculate m1 * m2 and the result will be in result. For the multiplication of each row, we can't create a Stream with Arrays.stream of the second matrix since this would create a Stream over the rows when we need a Stream over the columns. To counteract that, we simply go back to using an IntStream over the indexes.

like image 163
Tunaki Avatar answered Dec 07 '25 13:12

Tunaki


I created a BiFunction that performs the multiplication using IntStream.range(). If anyone has anything more compact I would love to see it. Here it is:

public static BiFunction<ArrayMatrix, ArrayMatrix, ArrayMatrix> multiply(boolean parallel) {
    return (m1, m2) -> {
        // checkMultiplicationCompatible(m1, m2);
        final int m1Rows = m1.getRowDimension();
        final int m2Rows = m2.getRowDimension();
        final int m1Cols = m1.getColumnDimension();
        final int m2Cols = m2.getColumnDimension();

        double[][] a1 = m1.getData();
        double[][] a2 = m2.getData();

        final double[][] result = new double[m1Rows][m2Cols];

        // Buffer for the tranpose of each md2 column
        final double[] transpose = new double[m1Rows];

        range(0, m2Cols).forEach(m2Col -> {
            range(0, m2Rows).forEach(m2Row -> {
                transpose[m2Row] = a2[m2Row][m2Col];
            });
            range(0, m1Rows).forEach(row -> {
                final double[] dataRow = a1[row];
                double sum = 0;
                for (int m1Col = 0; m1Col < m1Cols; m1Col++) {
                    sum += dataRow[m1Col] * transpose[m1Col];
                }
                result[row][m2Col] = sum;
            });
        });
        return new ArrayMatrix(result, false);
    };
}
like image 26
Ole Avatar answered Dec 07 '25 13:12

Ole



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!