/*
 * Copyright (C) 2010 by Frode Carlsen
 *
 * This file is part of the Universal Java Matrix Package (UJMP).
 * See the NOTICE file distributed with this work for additional
 * information regarding copyright ownership and licensing.
 *
 * UJMP is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * UJMP is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with UJMP; if not, write to the
 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
 * Boston, MA  02110-1301  USA
 */
package org.ujmp.core.doublematrix.impl;

import static org.ujmp.core.util.VerifyUtil.verifyTrue;

import java.util.concurrent.Callable;

/**
 * Multiply blocks of A and B in the specified range(fromM->toM, fromN->toN,
 * fromK->toK), 
* and add to matrix C. *

* * C(fromM->toM, fromK->toK) +=
       * A(fromM->toM, fromN->toN) x B(fromN->toN, fromK->toK) *
*

* All blocks must be square blocks of the same size, with length of one side = * {@link #blockStripeSize} * * @author Frode Carlsen */ public class BlockMultiply implements Callable { /** Length of one side of a block of data. */ private final int blockStripeSize; /** range of data in matrix to be processed by this instance. */ private final int fromM, toM, fromN, toN, fromK, toK; /** Source matrices to be processed. */ private final BlockDenseDoubleMatrix2D matrixA, matrixB, matrixC; /** * Constructor taking the two matrices being multiplied, the target matrix C * and the range of rows and columns to multiply. * * @param a * - matrix A, size (M, N) * @param b * - matrix B, size (N, K) * @param c * - result matrix C, size (M, K) * @param fromM * - start row M in matrix A * @param toM * - end row M in A * @param fromN * - start column N in A (or start row N in B) * @param toN * - end row N * @param fromK * - start column K in B * @param toK * - end column K in B */ public BlockMultiply(final BlockDenseDoubleMatrix2D a, final BlockDenseDoubleMatrix2D b, final BlockDenseDoubleMatrix2D c, final int fromM, final int toM, final int fromN, final int toN, final int fromK, final int toK) { super(); verifyInput(a, b, c, fromM, toM, fromN, toN, fromK, toK); this.matrixA = a; this.matrixB = b; this.matrixC = c; this.fromM = fromM; this.toM = toM; this.fromN = fromN; this.toN = toN; this.fromK = fromK; this.toK = toK; this.blockStripeSize = a.layout.blockStripe; } public Void call() { multiply(); return null; } /** * Multiply blocks of two matrices A,B and add to C. *

* Blocks of Matrix B are transformed to column-major layout (if not * already) to facilitate multiplication.
* (If matrices have been created optimally, B should already be * column-major) */ protected final void multiply() { final int step = blockStripeSize, blockSize = blockStripeSize * blockStripeSize; for (int m = fromM; m < toM; m += step) { final int aRows = matrixA.layout.getRowsInBlock(m); for (int k = fromK; k < toK; k += step) { final int bCols = matrixB.layout.getColumnsInBlock(k); final double[] cBlock = new double[aRows * bCols]; for (int n = fromN; n < toN; n += step) { // ensure a and b are in optimal block order before // multiplication final double[] aBlock = matrixA.layout.toRowMajorBlock(matrixA, m, n); final double[] bBlock = matrixB.layout.toColMajorBlock(matrixB, n, k); if (aBlock != null && bBlock != null) { if (aBlock.length == blockSize && bBlock.length == blockSize) { multiplyAxB(aBlock, bBlock, cBlock, step); } else { int aCols = aBlock.length / aRows; int bRows = bBlock.length / bCols; verifyTrue(aCols == bRows, "aCols!=bRows"); multiplyRowMajorTimesColumnMajorBlocks(aBlock, bBlock, cBlock, aRows, aCols, bCols); } } } matrixC.addBlockData(m, k, cBlock); } } } /** * Multiply row-major block (a) x column-major block (b), and add to block * c. * * @param a * - block from {@link #matrixA} * @param b * - block from {@link #matrixB} * @param c * - block from result matrix {@link #matrixC} */ private static void multiplyAxB(final double[] aBlock, final double[] bBlock, final double[] cBlock, final int step) { final int blockStripeMini = step % 3; final int blockStripeMaxi = step / 3; final int blockArea = step * step; for (int iL = 0; iL < blockArea; iL += step) { int rc = iL; for (int kL = 0; kL < blockArea; kL += step) { int ra = iL; int rb = kL; double sum = 0.0d; for (int jL = blockStripeMini; --jL >= 0;) { sum += aBlock[ra++] * bBlock[rb++]; } // loop unrolling for (int jL = blockStripeMaxi; --jL >= 0;) { sum += aBlock[ra++] * bBlock[rb++] // + aBlock[ra++] * bBlock[rb++] // + aBlock[ra++] * bBlock[rb++]; } cBlock[rc++] += sum; } } } public void multiplyRowMajorTimesColumnMajorBlocks(double[] aBlock, double[] bBlock, double[] cBlock, int aRows, int bRows, int bCols) { final int aCols = bRows; for (int i = 0; i < aRows; i++) { for (int k = 0; k < bCols; k++) { double sum = 0.0d; for (int j = 0; j < bRows; j++) { sum += aBlock[i * aCols + j] * bBlock[k * bRows + j]; } cBlock[i * bCols + k] += sum; } } } private static void verifyInput(final BlockDenseDoubleMatrix2D a, final BlockDenseDoubleMatrix2D b, final BlockDenseDoubleMatrix2D c, final int fromM, final int toM, final int fromN, final int toN, final int fromK, final int toK) { verifyTrue(a != null, "a cannot be null"); verifyTrue(b != null, "b cannot be null"); verifyTrue(c != null, "c cannot be null"); verifyTrue(fromM <= a.getRowCount() && fromM >= 0, "Invalid argument : fromM"); verifyTrue(toM <= a.getRowCount() && toM >= fromM, "Invalid argument : fromM/toM"); verifyTrue(fromN <= a.getColumnCount() && fromN >= 0, "Invalid argument : fromN"); verifyTrue(toN <= a.getColumnCount() && toN >= fromN, "Invalid argument : fromN/toN"); verifyTrue(fromK <= b.getColumnCount() && fromK >= 0, "Invalid argument : fromK"); verifyTrue(toK <= b.getColumnCount() && toK >= fromK, "Invalid argument : fromK/toK"); verifyTrue(a.getColumnCount() == b.getRowCount(), "Invalid argument : a.columns != b.rows"); verifyTrue(a.getRowCount() == c.getRowCount(), "Invalid argument : a.rows != c.rows"); verifyTrue(b.getColumnCount() == c.getColumnCount(), "Invalid argument : b.columns != c.columns"); } }

Ads help maintain this website.