001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 018package org.apache.commons.math3.linear; 019 020import java.io.Serializable; 021import java.util.Arrays; 022 023import org.apache.commons.math3.exception.DimensionMismatchException; 024import org.apache.commons.math3.exception.NoDataException; 025import org.apache.commons.math3.exception.NotStrictlyPositiveException; 026import org.apache.commons.math3.exception.NullArgumentException; 027import org.apache.commons.math3.exception.NumberIsTooSmallException; 028import org.apache.commons.math3.exception.OutOfRangeException; 029import org.apache.commons.math3.exception.util.LocalizedFormats; 030import org.apache.commons.math3.util.FastMath; 031import org.apache.commons.math3.util.MathUtils; 032 033/** 034 * Cache-friendly implementation of RealMatrix using a flat arrays to store 035 * square blocks of the matrix. 036 * <p> 037 * This implementation is specially designed to be cache-friendly. Square blocks are 038 * stored as small arrays and allow efficient traversal of data both in row major direction 039 * and columns major direction, one block at a time. This greatly increases performances 040 * for algorithms that use crossed directions loops like multiplication or transposition. 041 * </p> 042 * <p> 043 * The size of square blocks is a static parameter. It may be tuned according to the cache 044 * size of the target computer processor. As a rule of thumbs, it should be the largest 045 * value that allows three blocks to be simultaneously cached (this is necessary for example 046 * for matrix multiplication). The default value is to use 52x52 blocks which is well suited 047 * for processors with 64k L1 cache (one block holds 2704 values or 21632 bytes). This value 048 * could be lowered to 36x36 for processors with 32k L1 cache. 049 * </p> 050 * <p> 051 * The regular blocks represent {@link #BLOCK_SIZE} x {@link #BLOCK_SIZE} squares. Blocks 052 * at right hand side and bottom side which may be smaller to fit matrix dimensions. The square 053 * blocks are flattened in row major order in single dimension arrays which are therefore 054 * {@link #BLOCK_SIZE}<sup>2</sup> elements long for regular blocks. The blocks are themselves 055 * organized in row major order. 056 * </p> 057 * <p> 058 * As an example, for a block size of 52x52, a 100x60 matrix would be stored in 4 blocks. 059 * Block 0 would be a double[2704] array holding the upper left 52x52 square, block 1 would be 060 * a double[416] array holding the upper right 52x8 rectangle, block 2 would be a double[2496] 061 * array holding the lower left 48x52 rectangle and block 3 would be a double[384] array 062 * holding the lower right 48x8 rectangle. 063 * </p> 064 * <p> 065 * The layout complexity overhead versus simple mapping of matrices to java 066 * arrays is negligible for small matrices (about 1%). The gain from cache efficiency leads 067 * to up to 3-fold improvements for matrices of moderate to large size. 068 * </p> 069 * @since 2.0 070 */ 071public class BlockRealMatrix extends AbstractRealMatrix implements Serializable { 072 /** Block size. */ 073 public static final int BLOCK_SIZE = 52; 074 /** Serializable version identifier */ 075 private static final long serialVersionUID = 4991895511313664478L; 076 /** Blocks of matrix entries. */ 077 private final double blocks[][]; 078 /** Number of rows of the matrix. */ 079 private final int rows; 080 /** Number of columns of the matrix. */ 081 private final int columns; 082 /** Number of block rows of the matrix. */ 083 private final int blockRows; 084 /** Number of block columns of the matrix. */ 085 private final int blockColumns; 086 087 /** 088 * Create a new matrix with the supplied row and column dimensions. 089 * 090 * @param rows the number of rows in the new matrix 091 * @param columns the number of columns in the new matrix 092 * @throws NotStrictlyPositiveException if row or column dimension is not 093 * positive. 094 */ 095 public BlockRealMatrix(final int rows, final int columns) 096 throws NotStrictlyPositiveException { 097 super(rows, columns); 098 this.rows = rows; 099 this.columns = columns; 100 101 // number of blocks 102 blockRows = (rows + BLOCK_SIZE - 1) / BLOCK_SIZE; 103 blockColumns = (columns + BLOCK_SIZE - 1) / BLOCK_SIZE; 104 105 // allocate storage blocks, taking care of smaller ones at right and bottom 106 blocks = createBlocksLayout(rows, columns); 107 } 108 109 /** 110 * Create a new dense matrix copying entries from raw layout data. 111 * <p>The input array <em>must</em> already be in raw layout.</p> 112 * <p>Calling this constructor is equivalent to call: 113 * <pre>matrix = new BlockRealMatrix(rawData.length, rawData[0].length, 114 * toBlocksLayout(rawData), false);</pre> 115 * </p> 116 * 117 * @param rawData data for new matrix, in raw layout 118 * @throws DimensionMismatchException if the shape of {@code blockData} is 119 * inconsistent with block layout. 120 * @throws NotStrictlyPositiveException if row or column dimension is not 121 * positive. 122 * @see #BlockRealMatrix(int, int, double[][], boolean) 123 */ 124 public BlockRealMatrix(final double[][] rawData) 125 throws DimensionMismatchException, NotStrictlyPositiveException { 126 this(rawData.length, rawData[0].length, toBlocksLayout(rawData), false); 127 } 128 129 /** 130 * Create a new dense matrix copying entries from block layout data. 131 * <p>The input array <em>must</em> already be in blocks layout.</p> 132 * 133 * @param rows Number of rows in the new matrix. 134 * @param columns Number of columns in the new matrix. 135 * @param blockData data for new matrix 136 * @param copyArray Whether the input array will be copied or referenced. 137 * @throws DimensionMismatchException if the shape of {@code blockData} is 138 * inconsistent with block layout. 139 * @throws NotStrictlyPositiveException if row or column dimension is not 140 * positive. 141 * @see #createBlocksLayout(int, int) 142 * @see #toBlocksLayout(double[][]) 143 * @see #BlockRealMatrix(double[][]) 144 */ 145 public BlockRealMatrix(final int rows, final int columns, 146 final double[][] blockData, final boolean copyArray) 147 throws DimensionMismatchException, NotStrictlyPositiveException { 148 super(rows, columns); 149 this.rows = rows; 150 this.columns = columns; 151 152 // number of blocks 153 blockRows = (rows + BLOCK_SIZE - 1) / BLOCK_SIZE; 154 blockColumns = (columns + BLOCK_SIZE - 1) / BLOCK_SIZE; 155 156 if (copyArray) { 157 // allocate storage blocks, taking care of smaller ones at right and bottom 158 blocks = new double[blockRows * blockColumns][]; 159 } else { 160 // reference existing array 161 blocks = blockData; 162 } 163 164 int index = 0; 165 for (int iBlock = 0; iBlock < blockRows; ++iBlock) { 166 final int iHeight = blockHeight(iBlock); 167 for (int jBlock = 0; jBlock < blockColumns; ++jBlock, ++index) { 168 if (blockData[index].length != iHeight * blockWidth(jBlock)) { 169 throw new DimensionMismatchException(blockData[index].length, 170 iHeight * blockWidth(jBlock)); 171 } 172 if (copyArray) { 173 blocks[index] = blockData[index].clone(); 174 } 175 } 176 } 177 } 178 179 /** 180 * Convert a data array from raw layout to blocks layout. 181 * <p> 182 * Raw layout is the straightforward layout where element at row i and 183 * column j is in array element <code>rawData[i][j]</code>. Blocks layout 184 * is the layout used in {@link BlockRealMatrix} instances, where the matrix 185 * is split in square blocks (except at right and bottom side where blocks may 186 * be rectangular to fit matrix size) and each block is stored in a flattened 187 * one-dimensional array. 188 * </p> 189 * <p> 190 * This method creates an array in blocks layout from an input array in raw layout. 191 * It can be used to provide the array argument of the {@link 192 * #BlockRealMatrix(int, int, double[][], boolean)} constructor. 193 * </p> 194 * @param rawData Data array in raw layout. 195 * @return a new data array containing the same entries but in blocks layout. 196 * @throws DimensionMismatchException if {@code rawData} is not rectangular. 197 * @see #createBlocksLayout(int, int) 198 * @see #BlockRealMatrix(int, int, double[][], boolean) 199 */ 200 public static double[][] toBlocksLayout(final double[][] rawData) 201 throws DimensionMismatchException { 202 final int rows = rawData.length; 203 final int columns = rawData[0].length; 204 final int blockRows = (rows + BLOCK_SIZE - 1) / BLOCK_SIZE; 205 final int blockColumns = (columns + BLOCK_SIZE - 1) / BLOCK_SIZE; 206 207 // safety checks 208 for (int i = 0; i < rawData.length; ++i) { 209 final int length = rawData[i].length; 210 if (length != columns) { 211 throw new DimensionMismatchException(columns, length); 212 } 213 } 214 215 // convert array 216 final double[][] blocks = new double[blockRows * blockColumns][]; 217 int blockIndex = 0; 218 for (int iBlock = 0; iBlock < blockRows; ++iBlock) { 219 final int pStart = iBlock * BLOCK_SIZE; 220 final int pEnd = FastMath.min(pStart + BLOCK_SIZE, rows); 221 final int iHeight = pEnd - pStart; 222 for (int jBlock = 0; jBlock < blockColumns; ++jBlock) { 223 final int qStart = jBlock * BLOCK_SIZE; 224 final int qEnd = FastMath.min(qStart + BLOCK_SIZE, columns); 225 final int jWidth = qEnd - qStart; 226 227 // allocate new block 228 final double[] block = new double[iHeight * jWidth]; 229 blocks[blockIndex] = block; 230 231 // copy data 232 int index = 0; 233 for (int p = pStart; p < pEnd; ++p) { 234 System.arraycopy(rawData[p], qStart, block, index, jWidth); 235 index += jWidth; 236 } 237 ++blockIndex; 238 } 239 } 240 241 return blocks; 242 } 243 244 /** 245 * Create a data array in blocks layout. 246 * <p> 247 * This method can be used to create the array argument of the {@link 248 * #BlockRealMatrix(int, int, double[][], boolean)} constructor. 249 * </p> 250 * @param rows Number of rows in the new matrix. 251 * @param columns Number of columns in the new matrix. 252 * @return a new data array in blocks layout. 253 * @see #toBlocksLayout(double[][]) 254 * @see #BlockRealMatrix(int, int, double[][], boolean) 255 */ 256 public static double[][] createBlocksLayout(final int rows, final int columns) { 257 final int blockRows = (rows + BLOCK_SIZE - 1) / BLOCK_SIZE; 258 final int blockColumns = (columns + BLOCK_SIZE - 1) / BLOCK_SIZE; 259 260 final double[][] blocks = new double[blockRows * blockColumns][]; 261 int blockIndex = 0; 262 for (int iBlock = 0; iBlock < blockRows; ++iBlock) { 263 final int pStart = iBlock * BLOCK_SIZE; 264 final int pEnd = FastMath.min(pStart + BLOCK_SIZE, rows); 265 final int iHeight = pEnd - pStart; 266 for (int jBlock = 0; jBlock < blockColumns; ++jBlock) { 267 final int qStart = jBlock * BLOCK_SIZE; 268 final int qEnd = FastMath.min(qStart + BLOCK_SIZE, columns); 269 final int jWidth = qEnd - qStart; 270 blocks[blockIndex] = new double[iHeight * jWidth]; 271 ++blockIndex; 272 } 273 } 274 275 return blocks; 276 } 277 278 /** {@inheritDoc} */ 279 @Override 280 public BlockRealMatrix createMatrix(final int rowDimension, 281 final int columnDimension) 282 throws NotStrictlyPositiveException { 283 return new BlockRealMatrix(rowDimension, columnDimension); 284 } 285 286 /** {@inheritDoc} */ 287 @Override 288 public BlockRealMatrix copy() { 289 // create an empty matrix 290 BlockRealMatrix copied = new BlockRealMatrix(rows, columns); 291 292 // copy the blocks 293 for (int i = 0; i < blocks.length; ++i) { 294 System.arraycopy(blocks[i], 0, copied.blocks[i], 0, blocks[i].length); 295 } 296 297 return copied; 298 } 299 300 /** {@inheritDoc} */ 301 @Override 302 public BlockRealMatrix add(final RealMatrix m) 303 throws MatrixDimensionMismatchException { 304 try { 305 return add((BlockRealMatrix) m); 306 } catch (ClassCastException cce) { 307 // safety check 308 MatrixUtils.checkAdditionCompatible(this, m); 309 310 final BlockRealMatrix out = new BlockRealMatrix(rows, columns); 311 312 // perform addition block-wise, to ensure good cache behavior 313 int blockIndex = 0; 314 for (int iBlock = 0; iBlock < out.blockRows; ++iBlock) { 315 for (int jBlock = 0; jBlock < out.blockColumns; ++jBlock) { 316 317 // perform addition on the current block 318 final double[] outBlock = out.blocks[blockIndex]; 319 final double[] tBlock = blocks[blockIndex]; 320 final int pStart = iBlock * BLOCK_SIZE; 321 final int pEnd = FastMath.min(pStart + BLOCK_SIZE, rows); 322 final int qStart = jBlock * BLOCK_SIZE; 323 final int qEnd = FastMath.min(qStart + BLOCK_SIZE, columns); 324 int k = 0; 325 for (int p = pStart; p < pEnd; ++p) { 326 for (int q = qStart; q < qEnd; ++q) { 327 outBlock[k] = tBlock[k] + m.getEntry(p, q); 328 ++k; 329 } 330 } 331 // go to next block 332 ++blockIndex; 333 } 334 } 335 336 return out; 337 } 338 } 339 340 /** 341 * Compute the sum of this matrix and {@code m}. 342 * 343 * @param m Matrix to be added. 344 * @return {@code this} + m. 345 * @throws MatrixDimensionMismatchException if {@code m} is not the same 346 * size as this matrix. 347 */ 348 public BlockRealMatrix add(final BlockRealMatrix m) 349 throws MatrixDimensionMismatchException { 350 // safety check 351 MatrixUtils.checkAdditionCompatible(this, m); 352 353 final BlockRealMatrix out = new BlockRealMatrix(rows, columns); 354 355 // perform addition block-wise, to ensure good cache behavior 356 for (int blockIndex = 0; blockIndex < out.blocks.length; ++blockIndex) { 357 final double[] outBlock = out.blocks[blockIndex]; 358 final double[] tBlock = blocks[blockIndex]; 359 final double[] mBlock = m.blocks[blockIndex]; 360 for (int k = 0; k < outBlock.length; ++k) { 361 outBlock[k] = tBlock[k] + mBlock[k]; 362 } 363 } 364 365 return out; 366 } 367 368 /** {@inheritDoc} */ 369 @Override 370 public BlockRealMatrix subtract(final RealMatrix m) 371 throws MatrixDimensionMismatchException { 372 try { 373 return subtract((BlockRealMatrix) m); 374 } catch (ClassCastException cce) { 375 // safety check 376 MatrixUtils.checkSubtractionCompatible(this, m); 377 378 final BlockRealMatrix out = new BlockRealMatrix(rows, columns); 379 380 // perform subtraction block-wise, to ensure good cache behavior 381 int blockIndex = 0; 382 for (int iBlock = 0; iBlock < out.blockRows; ++iBlock) { 383 for (int jBlock = 0; jBlock < out.blockColumns; ++jBlock) { 384 385 // perform subtraction on the current block 386 final double[] outBlock = out.blocks[blockIndex]; 387 final double[] tBlock = blocks[blockIndex]; 388 final int pStart = iBlock * BLOCK_SIZE; 389 final int pEnd = FastMath.min(pStart + BLOCK_SIZE, rows); 390 final int qStart = jBlock * BLOCK_SIZE; 391 final int qEnd = FastMath.min(qStart + BLOCK_SIZE, columns); 392 int k = 0; 393 for (int p = pStart; p < pEnd; ++p) { 394 for (int q = qStart; q < qEnd; ++q) { 395 outBlock[k] = tBlock[k] - m.getEntry(p, q); 396 ++k; 397 } 398 } 399 // go to next block 400 ++blockIndex; 401 } 402 } 403 404 return out; 405 } 406 } 407 408 /** 409 * Subtract {@code m} from this matrix. 410 * 411 * @param m Matrix to be subtracted. 412 * @return {@code this} - m. 413 * @throws MatrixDimensionMismatchException if {@code m} is not the 414 * same size as this matrix. 415 */ 416 public BlockRealMatrix subtract(final BlockRealMatrix m) 417 throws MatrixDimensionMismatchException { 418 // safety check 419 MatrixUtils.checkSubtractionCompatible(this, m); 420 421 final BlockRealMatrix out = new BlockRealMatrix(rows, columns); 422 423 // perform subtraction block-wise, to ensure good cache behavior 424 for (int blockIndex = 0; blockIndex < out.blocks.length; ++blockIndex) { 425 final double[] outBlock = out.blocks[blockIndex]; 426 final double[] tBlock = blocks[blockIndex]; 427 final double[] mBlock = m.blocks[blockIndex]; 428 for (int k = 0; k < outBlock.length; ++k) { 429 outBlock[k] = tBlock[k] - mBlock[k]; 430 } 431 } 432 433 return out; 434 } 435 436 /** {@inheritDoc} */ 437 @Override 438 public BlockRealMatrix scalarAdd(final double d) { 439 440 final BlockRealMatrix out = new BlockRealMatrix(rows, columns); 441 442 // perform subtraction block-wise, to ensure good cache behavior 443 for (int blockIndex = 0; blockIndex < out.blocks.length; ++blockIndex) { 444 final double[] outBlock = out.blocks[blockIndex]; 445 final double[] tBlock = blocks[blockIndex]; 446 for (int k = 0; k < outBlock.length; ++k) { 447 outBlock[k] = tBlock[k] + d; 448 } 449 } 450 451 return out; 452 } 453 454 /** {@inheritDoc} */ 455 @Override 456 public RealMatrix scalarMultiply(final double d) { 457 final BlockRealMatrix out = new BlockRealMatrix(rows, columns); 458 459 // perform subtraction block-wise, to ensure good cache behavior 460 for (int blockIndex = 0; blockIndex < out.blocks.length; ++blockIndex) { 461 final double[] outBlock = out.blocks[blockIndex]; 462 final double[] tBlock = blocks[blockIndex]; 463 for (int k = 0; k < outBlock.length; ++k) { 464 outBlock[k] = tBlock[k] * d; 465 } 466 } 467 468 return out; 469 } 470 471 /** {@inheritDoc} */ 472 @Override 473 public BlockRealMatrix multiply(final RealMatrix m) 474 throws DimensionMismatchException { 475 try { 476 return multiply((BlockRealMatrix) m); 477 } catch (ClassCastException cce) { 478 // safety check 479 MatrixUtils.checkMultiplicationCompatible(this, m); 480 481 final BlockRealMatrix out = new BlockRealMatrix(rows, m.getColumnDimension()); 482 483 // perform multiplication block-wise, to ensure good cache behavior 484 int blockIndex = 0; 485 for (int iBlock = 0; iBlock < out.blockRows; ++iBlock) { 486 final int pStart = iBlock * BLOCK_SIZE; 487 final int pEnd = FastMath.min(pStart + BLOCK_SIZE, rows); 488 489 for (int jBlock = 0; jBlock < out.blockColumns; ++jBlock) { 490 final int qStart = jBlock * BLOCK_SIZE; 491 final int qEnd = FastMath.min(qStart + BLOCK_SIZE, m.getColumnDimension()); 492 493 // select current block 494 final double[] outBlock = out.blocks[blockIndex]; 495 496 // perform multiplication on current block 497 for (int kBlock = 0; kBlock < blockColumns; ++kBlock) { 498 final int kWidth = blockWidth(kBlock); 499 final double[] tBlock = blocks[iBlock * blockColumns + kBlock]; 500 final int rStart = kBlock * BLOCK_SIZE; 501 int k = 0; 502 for (int p = pStart; p < pEnd; ++p) { 503 final int lStart = (p - pStart) * kWidth; 504 final int lEnd = lStart + kWidth; 505 for (int q = qStart; q < qEnd; ++q) { 506 double sum = 0; 507 int r = rStart; 508 for (int l = lStart; l < lEnd; ++l) { 509 sum += tBlock[l] * m.getEntry(r, q); 510 ++r; 511 } 512 outBlock[k] += sum; 513 ++k; 514 } 515 } 516 } 517 // go to next block 518 ++blockIndex; 519 } 520 } 521 522 return out; 523 } 524 } 525 526 /** 527 * Returns the result of postmultiplying this by {@code m}. 528 * 529 * @param m Matrix to postmultiply by. 530 * @return {@code this} * m. 531 * @throws DimensionMismatchException if the matrices are not compatible. 532 */ 533 public BlockRealMatrix multiply(BlockRealMatrix m) 534 throws DimensionMismatchException { 535 // safety check 536 MatrixUtils.checkMultiplicationCompatible(this, m); 537 538 final BlockRealMatrix out = new BlockRealMatrix(rows, m.columns); 539 540 // perform multiplication block-wise, to ensure good cache behavior 541 int blockIndex = 0; 542 for (int iBlock = 0; iBlock < out.blockRows; ++iBlock) { 543 544 final int pStart = iBlock * BLOCK_SIZE; 545 final int pEnd = FastMath.min(pStart + BLOCK_SIZE, rows); 546 547 for (int jBlock = 0; jBlock < out.blockColumns; ++jBlock) { 548 final int jWidth = out.blockWidth(jBlock); 549 final int jWidth2 = jWidth + jWidth; 550 final int jWidth3 = jWidth2 + jWidth; 551 final int jWidth4 = jWidth3 + jWidth; 552 553 // select current block 554 final double[] outBlock = out.blocks[blockIndex]; 555 556 // perform multiplication on current block 557 for (int kBlock = 0; kBlock < blockColumns; ++kBlock) { 558 final int kWidth = blockWidth(kBlock); 559 final double[] tBlock = blocks[iBlock * blockColumns + kBlock]; 560 final double[] mBlock = m.blocks[kBlock * m.blockColumns + jBlock]; 561 int k = 0; 562 for (int p = pStart; p < pEnd; ++p) { 563 final int lStart = (p - pStart) * kWidth; 564 final int lEnd = lStart + kWidth; 565 for (int nStart = 0; nStart < jWidth; ++nStart) { 566 double sum = 0; 567 int l = lStart; 568 int n = nStart; 569 while (l < lEnd - 3) { 570 sum += tBlock[l] * mBlock[n] + 571 tBlock[l + 1] * mBlock[n + jWidth] + 572 tBlock[l + 2] * mBlock[n + jWidth2] + 573 tBlock[l + 3] * mBlock[n + jWidth3]; 574 l += 4; 575 n += jWidth4; 576 } 577 while (l < lEnd) { 578 sum += tBlock[l++] * mBlock[n]; 579 n += jWidth; 580 } 581 outBlock[k] += sum; 582 ++k; 583 } 584 } 585 } 586 // go to next block 587 ++blockIndex; 588 } 589 } 590 591 return out; 592 } 593 594 /** {@inheritDoc} */ 595 @Override 596 public double[][] getData() { 597 final double[][] data = new double[getRowDimension()][getColumnDimension()]; 598 final int lastColumns = columns - (blockColumns - 1) * BLOCK_SIZE; 599 600 for (int iBlock = 0; iBlock < blockRows; ++iBlock) { 601 final int pStart = iBlock * BLOCK_SIZE; 602 final int pEnd = FastMath.min(pStart + BLOCK_SIZE, rows); 603 int regularPos = 0; 604 int lastPos = 0; 605 for (int p = pStart; p < pEnd; ++p) { 606 final double[] dataP = data[p]; 607 int blockIndex = iBlock * blockColumns; 608 int dataPos = 0; 609 for (int jBlock = 0; jBlock < blockColumns - 1; ++jBlock) { 610 System.arraycopy(blocks[blockIndex++], regularPos, dataP, dataPos, BLOCK_SIZE); 611 dataPos += BLOCK_SIZE; 612 } 613 System.arraycopy(blocks[blockIndex], lastPos, dataP, dataPos, lastColumns); 614 regularPos += BLOCK_SIZE; 615 lastPos += lastColumns; 616 } 617 } 618 619 return data; 620 } 621 622 /** {@inheritDoc} */ 623 @Override 624 public double getNorm() { 625 final double[] colSums = new double[BLOCK_SIZE]; 626 double maxColSum = 0; 627 for (int jBlock = 0; jBlock < blockColumns; jBlock++) { 628 final int jWidth = blockWidth(jBlock); 629 Arrays.fill(colSums, 0, jWidth, 0.0); 630 for (int iBlock = 0; iBlock < blockRows; ++iBlock) { 631 final int iHeight = blockHeight(iBlock); 632 final double[] block = blocks[iBlock * blockColumns + jBlock]; 633 for (int j = 0; j < jWidth; ++j) { 634 double sum = 0; 635 for (int i = 0; i < iHeight; ++i) { 636 sum += FastMath.abs(block[i * jWidth + j]); 637 } 638 colSums[j] += sum; 639 } 640 } 641 for (int j = 0; j < jWidth; ++j) { 642 maxColSum = FastMath.max(maxColSum, colSums[j]); 643 } 644 } 645 return maxColSum; 646 } 647 648 /** {@inheritDoc} */ 649 @Override 650 public double getFrobeniusNorm() { 651 double sum2 = 0; 652 for (int blockIndex = 0; blockIndex < blocks.length; ++blockIndex) { 653 for (final double entry : blocks[blockIndex]) { 654 sum2 += entry * entry; 655 } 656 } 657 return FastMath.sqrt(sum2); 658 } 659 660 /** {@inheritDoc} */ 661 @Override 662 public BlockRealMatrix getSubMatrix(final int startRow, final int endRow, 663 final int startColumn, 664 final int endColumn) 665 throws OutOfRangeException, NumberIsTooSmallException { 666 // safety checks 667 MatrixUtils.checkSubMatrixIndex(this, startRow, endRow, startColumn, endColumn); 668 669 // create the output matrix 670 final BlockRealMatrix out = 671 new BlockRealMatrix(endRow - startRow + 1, endColumn - startColumn + 1); 672 673 // compute blocks shifts 674 final int blockStartRow = startRow / BLOCK_SIZE; 675 final int rowsShift = startRow % BLOCK_SIZE; 676 final int blockStartColumn = startColumn / BLOCK_SIZE; 677 final int columnsShift = startColumn % BLOCK_SIZE; 678 679 // perform extraction block-wise, to ensure good cache behavior 680 int pBlock = blockStartRow; 681 for (int iBlock = 0; iBlock < out.blockRows; ++iBlock) { 682 final int iHeight = out.blockHeight(iBlock); 683 int qBlock = blockStartColumn; 684 for (int jBlock = 0; jBlock < out.blockColumns; ++jBlock) { 685 final int jWidth = out.blockWidth(jBlock); 686 687 // handle one block of the output matrix 688 final int outIndex = iBlock * out.blockColumns + jBlock; 689 final double[] outBlock = out.blocks[outIndex]; 690 final int index = pBlock * blockColumns + qBlock; 691 final int width = blockWidth(qBlock); 692 693 final int heightExcess = iHeight + rowsShift - BLOCK_SIZE; 694 final int widthExcess = jWidth + columnsShift - BLOCK_SIZE; 695 if (heightExcess > 0) { 696 // the submatrix block spans on two blocks rows from the original matrix 697 if (widthExcess > 0) { 698 // the submatrix block spans on two blocks columns from the original matrix 699 final int width2 = blockWidth(qBlock + 1); 700 copyBlockPart(blocks[index], width, 701 rowsShift, BLOCK_SIZE, 702 columnsShift, BLOCK_SIZE, 703 outBlock, jWidth, 0, 0); 704 copyBlockPart(blocks[index + 1], width2, 705 rowsShift, BLOCK_SIZE, 706 0, widthExcess, 707 outBlock, jWidth, 0, jWidth - widthExcess); 708 copyBlockPart(blocks[index + blockColumns], width, 709 0, heightExcess, 710 columnsShift, BLOCK_SIZE, 711 outBlock, jWidth, iHeight - heightExcess, 0); 712 copyBlockPart(blocks[index + blockColumns + 1], width2, 713 0, heightExcess, 714 0, widthExcess, 715 outBlock, jWidth, iHeight - heightExcess, jWidth - widthExcess); 716 } else { 717 // the submatrix block spans on one block column from the original matrix 718 copyBlockPart(blocks[index], width, 719 rowsShift, BLOCK_SIZE, 720 columnsShift, jWidth + columnsShift, 721 outBlock, jWidth, 0, 0); 722 copyBlockPart(blocks[index + blockColumns], width, 723 0, heightExcess, 724 columnsShift, jWidth + columnsShift, 725 outBlock, jWidth, iHeight - heightExcess, 0); 726 } 727 } else { 728 // the submatrix block spans on one block row from the original matrix 729 if (widthExcess > 0) { 730 // the submatrix block spans on two blocks columns from the original matrix 731 final int width2 = blockWidth(qBlock + 1); 732 copyBlockPart(blocks[index], width, 733 rowsShift, iHeight + rowsShift, 734 columnsShift, BLOCK_SIZE, 735 outBlock, jWidth, 0, 0); 736 copyBlockPart(blocks[index + 1], width2, 737 rowsShift, iHeight + rowsShift, 738 0, widthExcess, 739 outBlock, jWidth, 0, jWidth - widthExcess); 740 } else { 741 // the submatrix block spans on one block column from the original matrix 742 copyBlockPart(blocks[index], width, 743 rowsShift, iHeight + rowsShift, 744 columnsShift, jWidth + columnsShift, 745 outBlock, jWidth, 0, 0); 746 } 747 } 748 ++qBlock; 749 } 750 ++pBlock; 751 } 752 753 return out; 754 } 755 756 /** 757 * Copy a part of a block into another one 758 * <p>This method can be called only when the specified part fits in both 759 * blocks, no verification is done here.</p> 760 * @param srcBlock source block 761 * @param srcWidth source block width ({@link #BLOCK_SIZE} or smaller) 762 * @param srcStartRow start row in the source block 763 * @param srcEndRow end row (exclusive) in the source block 764 * @param srcStartColumn start column in the source block 765 * @param srcEndColumn end column (exclusive) in the source block 766 * @param dstBlock destination block 767 * @param dstWidth destination block width ({@link #BLOCK_SIZE} or smaller) 768 * @param dstStartRow start row in the destination block 769 * @param dstStartColumn start column in the destination block 770 */ 771 private void copyBlockPart(final double[] srcBlock, final int srcWidth, 772 final int srcStartRow, final int srcEndRow, 773 final int srcStartColumn, final int srcEndColumn, 774 final double[] dstBlock, final int dstWidth, 775 final int dstStartRow, final int dstStartColumn) { 776 final int length = srcEndColumn - srcStartColumn; 777 int srcPos = srcStartRow * srcWidth + srcStartColumn; 778 int dstPos = dstStartRow * dstWidth + dstStartColumn; 779 for (int srcRow = srcStartRow; srcRow < srcEndRow; ++srcRow) { 780 System.arraycopy(srcBlock, srcPos, dstBlock, dstPos, length); 781 srcPos += srcWidth; 782 dstPos += dstWidth; 783 } 784 } 785 786 /** {@inheritDoc} */ 787 @Override 788 public void setSubMatrix(final double[][] subMatrix, final int row, 789 final int column) 790 throws OutOfRangeException, NoDataException, NullArgumentException, 791 DimensionMismatchException { 792 // safety checks 793 MathUtils.checkNotNull(subMatrix); 794 final int refLength = subMatrix[0].length; 795 if (refLength == 0) { 796 throw new NoDataException(LocalizedFormats.AT_LEAST_ONE_COLUMN); 797 } 798 final int endRow = row + subMatrix.length - 1; 799 final int endColumn = column + refLength - 1; 800 MatrixUtils.checkSubMatrixIndex(this, row, endRow, column, endColumn); 801 for (final double[] subRow : subMatrix) { 802 if (subRow.length != refLength) { 803 throw new DimensionMismatchException(refLength, subRow.length); 804 } 805 } 806 807 // compute blocks bounds 808 final int blockStartRow = row / BLOCK_SIZE; 809 final int blockEndRow = (endRow + BLOCK_SIZE) / BLOCK_SIZE; 810 final int blockStartColumn = column / BLOCK_SIZE; 811 final int blockEndColumn = (endColumn + BLOCK_SIZE) / BLOCK_SIZE; 812 813 // perform copy block-wise, to ensure good cache behavior 814 for (int iBlock = blockStartRow; iBlock < blockEndRow; ++iBlock) { 815 final int iHeight = blockHeight(iBlock); 816 final int firstRow = iBlock * BLOCK_SIZE; 817 final int iStart = FastMath.max(row, firstRow); 818 final int iEnd = FastMath.min(endRow + 1, firstRow + iHeight); 819 820 for (int jBlock = blockStartColumn; jBlock < blockEndColumn; ++jBlock) { 821 final int jWidth = blockWidth(jBlock); 822 final int firstColumn = jBlock * BLOCK_SIZE; 823 final int jStart = FastMath.max(column, firstColumn); 824 final int jEnd = FastMath.min(endColumn + 1, firstColumn + jWidth); 825 final int jLength = jEnd - jStart; 826 827 // handle one block, row by row 828 final double[] block = blocks[iBlock * blockColumns + jBlock]; 829 for (int i = iStart; i < iEnd; ++i) { 830 System.arraycopy(subMatrix[i - row], jStart - column, 831 block, (i - firstRow) * jWidth + (jStart - firstColumn), 832 jLength); 833 } 834 835 } 836 } 837 } 838 839 /** {@inheritDoc} */ 840 @Override 841 public BlockRealMatrix getRowMatrix(final int row) 842 throws OutOfRangeException { 843 MatrixUtils.checkRowIndex(this, row); 844 final BlockRealMatrix out = new BlockRealMatrix(1, columns); 845 846 // perform copy block-wise, to ensure good cache behavior 847 final int iBlock = row / BLOCK_SIZE; 848 final int iRow = row - iBlock * BLOCK_SIZE; 849 int outBlockIndex = 0; 850 int outIndex = 0; 851 double[] outBlock = out.blocks[outBlockIndex]; 852 for (int jBlock = 0; jBlock < blockColumns; ++jBlock) { 853 final int jWidth = blockWidth(jBlock); 854 final double[] block = blocks[iBlock * blockColumns + jBlock]; 855 final int available = outBlock.length - outIndex; 856 if (jWidth > available) { 857 System.arraycopy(block, iRow * jWidth, outBlock, outIndex, available); 858 outBlock = out.blocks[++outBlockIndex]; 859 System.arraycopy(block, iRow * jWidth, outBlock, 0, jWidth - available); 860 outIndex = jWidth - available; 861 } else { 862 System.arraycopy(block, iRow * jWidth, outBlock, outIndex, jWidth); 863 outIndex += jWidth; 864 } 865 } 866 867 return out; 868 } 869 870 /** {@inheritDoc} */ 871 @Override 872 public void setRowMatrix(final int row, final RealMatrix matrix) 873 throws OutOfRangeException, MatrixDimensionMismatchException { 874 try { 875 setRowMatrix(row, (BlockRealMatrix) matrix); 876 } catch (ClassCastException cce) { 877 super.setRowMatrix(row, matrix); 878 } 879 } 880 881 /** 882 * Sets the entries in row number <code>row</code> 883 * as a row matrix. Row indices start at 0. 884 * 885 * @param row the row to be set 886 * @param matrix row matrix (must have one row and the same number of columns 887 * as the instance) 888 * @throws OutOfRangeException if the specified row index is invalid. 889 * @throws MatrixDimensionMismatchException if the matrix dimensions do 890 * not match one instance row. 891 */ 892 public void setRowMatrix(final int row, final BlockRealMatrix matrix) 893 throws OutOfRangeException, MatrixDimensionMismatchException { 894 MatrixUtils.checkRowIndex(this, row); 895 final int nCols = getColumnDimension(); 896 if ((matrix.getRowDimension() != 1) || 897 (matrix.getColumnDimension() != nCols)) { 898 throw new MatrixDimensionMismatchException(matrix.getRowDimension(), 899 matrix.getColumnDimension(), 900 1, nCols); 901 } 902 903 // perform copy block-wise, to ensure good cache behavior 904 final int iBlock = row / BLOCK_SIZE; 905 final int iRow = row - iBlock * BLOCK_SIZE; 906 int mBlockIndex = 0; 907 int mIndex = 0; 908 double[] mBlock = matrix.blocks[mBlockIndex]; 909 for (int jBlock = 0; jBlock < blockColumns; ++jBlock) { 910 final int jWidth = blockWidth(jBlock); 911 final double[] block = blocks[iBlock * blockColumns + jBlock]; 912 final int available = mBlock.length - mIndex; 913 if (jWidth > available) { 914 System.arraycopy(mBlock, mIndex, block, iRow * jWidth, available); 915 mBlock = matrix.blocks[++mBlockIndex]; 916 System.arraycopy(mBlock, 0, block, iRow * jWidth, jWidth - available); 917 mIndex = jWidth - available; 918 } else { 919 System.arraycopy(mBlock, mIndex, block, iRow * jWidth, jWidth); 920 mIndex += jWidth; 921 } 922 } 923 } 924 925 /** {@inheritDoc} */ 926 @Override 927 public BlockRealMatrix getColumnMatrix(final int column) 928 throws OutOfRangeException { 929 MatrixUtils.checkColumnIndex(this, column); 930 final BlockRealMatrix out = new BlockRealMatrix(rows, 1); 931 932 // perform copy block-wise, to ensure good cache behavior 933 final int jBlock = column / BLOCK_SIZE; 934 final int jColumn = column - jBlock * BLOCK_SIZE; 935 final int jWidth = blockWidth(jBlock); 936 int outBlockIndex = 0; 937 int outIndex = 0; 938 double[] outBlock = out.blocks[outBlockIndex]; 939 for (int iBlock = 0; iBlock < blockRows; ++iBlock) { 940 final int iHeight = blockHeight(iBlock); 941 final double[] block = blocks[iBlock * blockColumns + jBlock]; 942 for (int i = 0; i < iHeight; ++i) { 943 if (outIndex >= outBlock.length) { 944 outBlock = out.blocks[++outBlockIndex]; 945 outIndex = 0; 946 } 947 outBlock[outIndex++] = block[i * jWidth + jColumn]; 948 } 949 } 950 951 return out; 952 } 953 954 /** {@inheritDoc} */ 955 @Override 956 public void setColumnMatrix(final int column, final RealMatrix matrix) 957 throws OutOfRangeException, MatrixDimensionMismatchException { 958 try { 959 setColumnMatrix(column, (BlockRealMatrix) matrix); 960 } catch (ClassCastException cce) { 961 super.setColumnMatrix(column, matrix); 962 } 963 } 964 965 /** 966 * Sets the entries in column number <code>column</code> 967 * as a column matrix. Column indices start at 0. 968 * 969 * @param column the column to be set 970 * @param matrix column matrix (must have one column and the same number of rows 971 * as the instance) 972 * @throws OutOfRangeException if the specified column index is invalid. 973 * @throws MatrixDimensionMismatchException if the matrix dimensions do 974 * not match one instance column. 975 */ 976 void setColumnMatrix(final int column, final BlockRealMatrix matrix) 977 throws OutOfRangeException, MatrixDimensionMismatchException { 978 MatrixUtils.checkColumnIndex(this, column); 979 final int nRows = getRowDimension(); 980 if ((matrix.getRowDimension() != nRows) || 981 (matrix.getColumnDimension() != 1)) { 982 throw new MatrixDimensionMismatchException(matrix.getRowDimension(), 983 matrix.getColumnDimension(), 984 nRows, 1); 985 } 986 987 // perform copy block-wise, to ensure good cache behavior 988 final int jBlock = column / BLOCK_SIZE; 989 final int jColumn = column - jBlock * BLOCK_SIZE; 990 final int jWidth = blockWidth(jBlock); 991 int mBlockIndex = 0; 992 int mIndex = 0; 993 double[] mBlock = matrix.blocks[mBlockIndex]; 994 for (int iBlock = 0; iBlock < blockRows; ++iBlock) { 995 final int iHeight = blockHeight(iBlock); 996 final double[] block = blocks[iBlock * blockColumns + jBlock]; 997 for (int i = 0; i < iHeight; ++i) { 998 if (mIndex >= mBlock.length) { 999 mBlock = matrix.blocks[++mBlockIndex]; 1000 mIndex = 0; 1001 } 1002 block[i * jWidth + jColumn] = mBlock[mIndex++]; 1003 } 1004 } 1005 } 1006 1007 /** {@inheritDoc} */ 1008 @Override 1009 public RealVector getRowVector(final int row) 1010 throws OutOfRangeException { 1011 MatrixUtils.checkRowIndex(this, row); 1012 final double[] outData = new double[columns]; 1013 1014 // perform copy block-wise, to ensure good cache behavior 1015 final int iBlock = row / BLOCK_SIZE; 1016 final int iRow = row - iBlock * BLOCK_SIZE; 1017 int outIndex = 0; 1018 for (int jBlock = 0; jBlock < blockColumns; ++jBlock) { 1019 final int jWidth = blockWidth(jBlock); 1020 final double[] block = blocks[iBlock * blockColumns + jBlock]; 1021 System.arraycopy(block, iRow * jWidth, outData, outIndex, jWidth); 1022 outIndex += jWidth; 1023 } 1024 1025 return new ArrayRealVector(outData, false); 1026 } 1027 1028 /** {@inheritDoc} */ 1029 @Override 1030 public void setRowVector(final int row, final RealVector vector) 1031 throws OutOfRangeException, MatrixDimensionMismatchException { 1032 try { 1033 setRow(row, ((ArrayRealVector) vector).getDataRef()); 1034 } catch (ClassCastException cce) { 1035 super.setRowVector(row, vector); 1036 } 1037 } 1038 1039 /** {@inheritDoc} */ 1040 @Override 1041 public RealVector getColumnVector(final int column) 1042 throws OutOfRangeException { 1043 MatrixUtils.checkColumnIndex(this, column); 1044 final double[] outData = new double[rows]; 1045 1046 // perform copy block-wise, to ensure good cache behavior 1047 final int jBlock = column / BLOCK_SIZE; 1048 final int jColumn = column - jBlock * BLOCK_SIZE; 1049 final int jWidth = blockWidth(jBlock); 1050 int outIndex = 0; 1051 for (int iBlock = 0; iBlock < blockRows; ++iBlock) { 1052 final int iHeight = blockHeight(iBlock); 1053 final double[] block = blocks[iBlock * blockColumns + jBlock]; 1054 for (int i = 0; i < iHeight; ++i) { 1055 outData[outIndex++] = block[i * jWidth + jColumn]; 1056 } 1057 } 1058 1059 return new ArrayRealVector(outData, false); 1060 } 1061 1062 /** {@inheritDoc} */ 1063 @Override 1064 public void setColumnVector(final int column, final RealVector vector) 1065 throws OutOfRangeException, MatrixDimensionMismatchException { 1066 try { 1067 setColumn(column, ((ArrayRealVector) vector).getDataRef()); 1068 } catch (ClassCastException cce) { 1069 super.setColumnVector(column, vector); 1070 } 1071 } 1072 1073 /** {@inheritDoc} */ 1074 @Override 1075 public double[] getRow(final int row) throws OutOfRangeException { 1076 MatrixUtils.checkRowIndex(this, row); 1077 final double[] out = new double[columns]; 1078 1079 // perform copy block-wise, to ensure good cache behavior 1080 final int iBlock = row / BLOCK_SIZE; 1081 final int iRow = row - iBlock * BLOCK_SIZE; 1082 int outIndex = 0; 1083 for (int jBlock = 0; jBlock < blockColumns; ++jBlock) { 1084 final int jWidth = blockWidth(jBlock); 1085 final double[] block = blocks[iBlock * blockColumns + jBlock]; 1086 System.arraycopy(block, iRow * jWidth, out, outIndex, jWidth); 1087 outIndex += jWidth; 1088 } 1089 1090 return out; 1091 } 1092 1093 /** {@inheritDoc} */ 1094 @Override 1095 public void setRow(final int row, final double[] array) 1096 throws OutOfRangeException, MatrixDimensionMismatchException { 1097 MatrixUtils.checkRowIndex(this, row); 1098 final int nCols = getColumnDimension(); 1099 if (array.length != nCols) { 1100 throw new MatrixDimensionMismatchException(1, array.length, 1, nCols); 1101 } 1102 1103 // perform copy block-wise, to ensure good cache behavior 1104 final int iBlock = row / BLOCK_SIZE; 1105 final int iRow = row - iBlock * BLOCK_SIZE; 1106 int outIndex = 0; 1107 for (int jBlock = 0; jBlock < blockColumns; ++jBlock) { 1108 final int jWidth = blockWidth(jBlock); 1109 final double[] block = blocks[iBlock * blockColumns + jBlock]; 1110 System.arraycopy(array, outIndex, block, iRow * jWidth, jWidth); 1111 outIndex += jWidth; 1112 } 1113 } 1114 1115 /** {@inheritDoc} */ 1116 @Override 1117 public double[] getColumn(final int column) throws OutOfRangeException { 1118 MatrixUtils.checkColumnIndex(this, column); 1119 final double[] out = new double[rows]; 1120 1121 // perform copy block-wise, to ensure good cache behavior 1122 final int jBlock = column / BLOCK_SIZE; 1123 final int jColumn = column - jBlock * BLOCK_SIZE; 1124 final int jWidth = blockWidth(jBlock); 1125 int outIndex = 0; 1126 for (int iBlock = 0; iBlock < blockRows; ++iBlock) { 1127 final int iHeight = blockHeight(iBlock); 1128 final double[] block = blocks[iBlock * blockColumns + jBlock]; 1129 for (int i = 0; i < iHeight; ++i) { 1130 out[outIndex++] = block[i * jWidth + jColumn]; 1131 } 1132 } 1133 1134 return out; 1135 } 1136 1137 /** {@inheritDoc} */ 1138 @Override 1139 public void setColumn(final int column, final double[] array) 1140 throws OutOfRangeException, MatrixDimensionMismatchException { 1141 MatrixUtils.checkColumnIndex(this, column); 1142 final int nRows = getRowDimension(); 1143 if (array.length != nRows) { 1144 throw new MatrixDimensionMismatchException(array.length, 1, nRows, 1); 1145 } 1146 1147 // perform copy block-wise, to ensure good cache behavior 1148 final int jBlock = column / BLOCK_SIZE; 1149 final int jColumn = column - jBlock * BLOCK_SIZE; 1150 final int jWidth = blockWidth(jBlock); 1151 int outIndex = 0; 1152 for (int iBlock = 0; iBlock < blockRows; ++iBlock) { 1153 final int iHeight = blockHeight(iBlock); 1154 final double[] block = blocks[iBlock * blockColumns + jBlock]; 1155 for (int i = 0; i < iHeight; ++i) { 1156 block[i * jWidth + jColumn] = array[outIndex++]; 1157 } 1158 } 1159 } 1160 1161 /** {@inheritDoc} */ 1162 @Override 1163 public double getEntry(final int row, final int column) 1164 throws OutOfRangeException { 1165 MatrixUtils.checkMatrixIndex(this, row, column); 1166 final int iBlock = row / BLOCK_SIZE; 1167 final int jBlock = column / BLOCK_SIZE; 1168 final int k = (row - iBlock * BLOCK_SIZE) * blockWidth(jBlock) + 1169 (column - jBlock * BLOCK_SIZE); 1170 return blocks[iBlock * blockColumns + jBlock][k]; 1171 } 1172 1173 /** {@inheritDoc} */ 1174 @Override 1175 public void setEntry(final int row, final int column, final double value) 1176 throws OutOfRangeException { 1177 MatrixUtils.checkMatrixIndex(this, row, column); 1178 final int iBlock = row / BLOCK_SIZE; 1179 final int jBlock = column / BLOCK_SIZE; 1180 final int k = (row - iBlock * BLOCK_SIZE) * blockWidth(jBlock) + 1181 (column - jBlock * BLOCK_SIZE); 1182 blocks[iBlock * blockColumns + jBlock][k] = value; 1183 } 1184 1185 /** {@inheritDoc} */ 1186 @Override 1187 public void addToEntry(final int row, final int column, 1188 final double increment) 1189 throws OutOfRangeException { 1190 MatrixUtils.checkMatrixIndex(this, row, column); 1191 final int iBlock = row / BLOCK_SIZE; 1192 final int jBlock = column / BLOCK_SIZE; 1193 final int k = (row - iBlock * BLOCK_SIZE) * blockWidth(jBlock) + 1194 (column - jBlock * BLOCK_SIZE); 1195 blocks[iBlock * blockColumns + jBlock][k] += increment; 1196 } 1197 1198 /** {@inheritDoc} */ 1199 @Override 1200 public void multiplyEntry(final int row, final int column, 1201 final double factor) 1202 throws OutOfRangeException { 1203 MatrixUtils.checkMatrixIndex(this, row, column); 1204 final int iBlock = row / BLOCK_SIZE; 1205 final int jBlock = column / BLOCK_SIZE; 1206 final int k = (row - iBlock * BLOCK_SIZE) * blockWidth(jBlock) + 1207 (column - jBlock * BLOCK_SIZE); 1208 blocks[iBlock * blockColumns + jBlock][k] *= factor; 1209 } 1210 1211 /** {@inheritDoc} */ 1212 @Override 1213 public BlockRealMatrix transpose() { 1214 final int nRows = getRowDimension(); 1215 final int nCols = getColumnDimension(); 1216 final BlockRealMatrix out = new BlockRealMatrix(nCols, nRows); 1217 1218 // perform transpose block-wise, to ensure good cache behavior 1219 int blockIndex = 0; 1220 for (int iBlock = 0; iBlock < blockColumns; ++iBlock) { 1221 for (int jBlock = 0; jBlock < blockRows; ++jBlock) { 1222 // transpose current block 1223 final double[] outBlock = out.blocks[blockIndex]; 1224 final double[] tBlock = blocks[jBlock * blockColumns + iBlock]; 1225 final int pStart = iBlock * BLOCK_SIZE; 1226 final int pEnd = FastMath.min(pStart + BLOCK_SIZE, columns); 1227 final int qStart = jBlock * BLOCK_SIZE; 1228 final int qEnd = FastMath.min(qStart + BLOCK_SIZE, rows); 1229 int k = 0; 1230 for (int p = pStart; p < pEnd; ++p) { 1231 final int lInc = pEnd - pStart; 1232 int l = p - pStart; 1233 for (int q = qStart; q < qEnd; ++q) { 1234 outBlock[k] = tBlock[l]; 1235 ++k; 1236 l+= lInc; 1237 } 1238 } 1239 // go to next block 1240 ++blockIndex; 1241 } 1242 } 1243 1244 return out; 1245 } 1246 1247 /** {@inheritDoc} */ 1248 @Override 1249 public int getRowDimension() { 1250 return rows; 1251 } 1252 1253 /** {@inheritDoc} */ 1254 @Override 1255 public int getColumnDimension() { 1256 return columns; 1257 } 1258 1259 /** {@inheritDoc} */ 1260 @Override 1261 public double[] operate(final double[] v) 1262 throws DimensionMismatchException { 1263 if (v.length != columns) { 1264 throw new DimensionMismatchException(v.length, columns); 1265 } 1266 final double[] out = new double[rows]; 1267 1268 // perform multiplication block-wise, to ensure good cache behavior 1269 for (int iBlock = 0; iBlock < blockRows; ++iBlock) { 1270 final int pStart = iBlock * BLOCK_SIZE; 1271 final int pEnd = FastMath.min(pStart + BLOCK_SIZE, rows); 1272 for (int jBlock = 0; jBlock < blockColumns; ++jBlock) { 1273 final double[] block = blocks[iBlock * blockColumns + jBlock]; 1274 final int qStart = jBlock * BLOCK_SIZE; 1275 final int qEnd = FastMath.min(qStart + BLOCK_SIZE, columns); 1276 int k = 0; 1277 for (int p = pStart; p < pEnd; ++p) { 1278 double sum = 0; 1279 int q = qStart; 1280 while (q < qEnd - 3) { 1281 sum += block[k] * v[q] + 1282 block[k + 1] * v[q + 1] + 1283 block[k + 2] * v[q + 2] + 1284 block[k + 3] * v[q + 3]; 1285 k += 4; 1286 q += 4; 1287 } 1288 while (q < qEnd) { 1289 sum += block[k++] * v[q++]; 1290 } 1291 out[p] += sum; 1292 } 1293 } 1294 } 1295 1296 return out; 1297 } 1298 1299 /** {@inheritDoc} */ 1300 @Override 1301 public double[] preMultiply(final double[] v) 1302 throws DimensionMismatchException { 1303 if (v.length != rows) { 1304 throw new DimensionMismatchException(v.length, rows); 1305 } 1306 final double[] out = new double[columns]; 1307 1308 // perform multiplication block-wise, to ensure good cache behavior 1309 for (int jBlock = 0; jBlock < blockColumns; ++jBlock) { 1310 final int jWidth = blockWidth(jBlock); 1311 final int jWidth2 = jWidth + jWidth; 1312 final int jWidth3 = jWidth2 + jWidth; 1313 final int jWidth4 = jWidth3 + jWidth; 1314 final int qStart = jBlock * BLOCK_SIZE; 1315 final int qEnd = FastMath.min(qStart + BLOCK_SIZE, columns); 1316 for (int iBlock = 0; iBlock < blockRows; ++iBlock) { 1317 final double[] block = blocks[iBlock * blockColumns + jBlock]; 1318 final int pStart = iBlock * BLOCK_SIZE; 1319 final int pEnd = FastMath.min(pStart + BLOCK_SIZE, rows); 1320 for (int q = qStart; q < qEnd; ++q) { 1321 int k = q - qStart; 1322 double sum = 0; 1323 int p = pStart; 1324 while (p < pEnd - 3) { 1325 sum += block[k] * v[p] + 1326 block[k + jWidth] * v[p + 1] + 1327 block[k + jWidth2] * v[p + 2] + 1328 block[k + jWidth3] * v[p + 3]; 1329 k += jWidth4; 1330 p += 4; 1331 } 1332 while (p < pEnd) { 1333 sum += block[k] * v[p++]; 1334 k += jWidth; 1335 } 1336 out[q] += sum; 1337 } 1338 } 1339 } 1340 1341 return out; 1342 } 1343 1344 /** {@inheritDoc} */ 1345 @Override 1346 public double walkInRowOrder(final RealMatrixChangingVisitor visitor) { 1347 visitor.start(rows, columns, 0, rows - 1, 0, columns - 1); 1348 for (int iBlock = 0; iBlock < blockRows; ++iBlock) { 1349 final int pStart = iBlock * BLOCK_SIZE; 1350 final int pEnd = FastMath.min(pStart + BLOCK_SIZE, rows); 1351 for (int p = pStart; p < pEnd; ++p) { 1352 for (int jBlock = 0; jBlock < blockColumns; ++jBlock) { 1353 final int jWidth = blockWidth(jBlock); 1354 final int qStart = jBlock * BLOCK_SIZE; 1355 final int qEnd = FastMath.min(qStart + BLOCK_SIZE, columns); 1356 final double[] block = blocks[iBlock * blockColumns + jBlock]; 1357 int k = (p - pStart) * jWidth; 1358 for (int q = qStart; q < qEnd; ++q) { 1359 block[k] = visitor.visit(p, q, block[k]); 1360 ++k; 1361 } 1362 } 1363 } 1364 } 1365 return visitor.end(); 1366 } 1367 1368 /** {@inheritDoc} */ 1369 @Override 1370 public double walkInRowOrder(final RealMatrixPreservingVisitor visitor) { 1371 visitor.start(rows, columns, 0, rows - 1, 0, columns - 1); 1372 for (int iBlock = 0; iBlock < blockRows; ++iBlock) { 1373 final int pStart = iBlock * BLOCK_SIZE; 1374 final int pEnd = FastMath.min(pStart + BLOCK_SIZE, rows); 1375 for (int p = pStart; p < pEnd; ++p) { 1376 for (int jBlock = 0; jBlock < blockColumns; ++jBlock) { 1377 final int jWidth = blockWidth(jBlock); 1378 final int qStart = jBlock * BLOCK_SIZE; 1379 final int qEnd = FastMath.min(qStart + BLOCK_SIZE, columns); 1380 final double[] block = blocks[iBlock * blockColumns + jBlock]; 1381 int k = (p - pStart) * jWidth; 1382 for (int q = qStart; q < qEnd; ++q) { 1383 visitor.visit(p, q, block[k]); 1384 ++k; 1385 } 1386 } 1387 } 1388 } 1389 return visitor.end(); 1390 } 1391 1392 /** {@inheritDoc} */ 1393 @Override 1394 public double walkInRowOrder(final RealMatrixChangingVisitor visitor, 1395 final int startRow, final int endRow, 1396 final int startColumn, final int endColumn) 1397 throws OutOfRangeException, NumberIsTooSmallException { 1398 MatrixUtils.checkSubMatrixIndex(this, startRow, endRow, startColumn, endColumn); 1399 visitor.start(rows, columns, startRow, endRow, startColumn, endColumn); 1400 for (int iBlock = startRow / BLOCK_SIZE; iBlock < 1 + endRow / BLOCK_SIZE; ++iBlock) { 1401 final int p0 = iBlock * BLOCK_SIZE; 1402 final int pStart = FastMath.max(startRow, p0); 1403 final int pEnd = FastMath.min((iBlock + 1) * BLOCK_SIZE, 1 + endRow); 1404 for (int p = pStart; p < pEnd; ++p) { 1405 for (int jBlock = startColumn / BLOCK_SIZE; jBlock < 1 + endColumn / BLOCK_SIZE; ++jBlock) { 1406 final int jWidth = blockWidth(jBlock); 1407 final int q0 = jBlock * BLOCK_SIZE; 1408 final int qStart = FastMath.max(startColumn, q0); 1409 final int qEnd = FastMath.min((jBlock + 1) * BLOCK_SIZE, 1 + endColumn); 1410 final double[] block = blocks[iBlock * blockColumns + jBlock]; 1411 int k = (p - p0) * jWidth + qStart - q0; 1412 for (int q = qStart; q < qEnd; ++q) { 1413 block[k] = visitor.visit(p, q, block[k]); 1414 ++k; 1415 } 1416 } 1417 } 1418 } 1419 return visitor.end(); 1420 } 1421 1422 /** {@inheritDoc} */ 1423 @Override 1424 public double walkInRowOrder(final RealMatrixPreservingVisitor visitor, 1425 final int startRow, final int endRow, 1426 final int startColumn, final int endColumn) 1427 throws OutOfRangeException, NumberIsTooSmallException { 1428 MatrixUtils.checkSubMatrixIndex(this, startRow, endRow, startColumn, endColumn); 1429 visitor.start(rows, columns, startRow, endRow, startColumn, endColumn); 1430 for (int iBlock = startRow / BLOCK_SIZE; iBlock < 1 + endRow / BLOCK_SIZE; ++iBlock) { 1431 final int p0 = iBlock * BLOCK_SIZE; 1432 final int pStart = FastMath.max(startRow, p0); 1433 final int pEnd = FastMath.min((iBlock + 1) * BLOCK_SIZE, 1 + endRow); 1434 for (int p = pStart; p < pEnd; ++p) { 1435 for (int jBlock = startColumn / BLOCK_SIZE; jBlock < 1 + endColumn / BLOCK_SIZE; ++jBlock) { 1436 final int jWidth = blockWidth(jBlock); 1437 final int q0 = jBlock * BLOCK_SIZE; 1438 final int qStart = FastMath.max(startColumn, q0); 1439 final int qEnd = FastMath.min((jBlock + 1) * BLOCK_SIZE, 1 + endColumn); 1440 final double[] block = blocks[iBlock * blockColumns + jBlock]; 1441 int k = (p - p0) * jWidth + qStart - q0; 1442 for (int q = qStart; q < qEnd; ++q) { 1443 visitor.visit(p, q, block[k]); 1444 ++k; 1445 } 1446 } 1447 } 1448 } 1449 return visitor.end(); 1450 } 1451 1452 /** {@inheritDoc} */ 1453 @Override 1454 public double walkInOptimizedOrder(final RealMatrixChangingVisitor visitor) { 1455 visitor.start(rows, columns, 0, rows - 1, 0, columns - 1); 1456 int blockIndex = 0; 1457 for (int iBlock = 0; iBlock < blockRows; ++iBlock) { 1458 final int pStart = iBlock * BLOCK_SIZE; 1459 final int pEnd = FastMath.min(pStart + BLOCK_SIZE, rows); 1460 for (int jBlock = 0; jBlock < blockColumns; ++jBlock) { 1461 final int qStart = jBlock * BLOCK_SIZE; 1462 final int qEnd = FastMath.min(qStart + BLOCK_SIZE, columns); 1463 final double[] block = blocks[blockIndex]; 1464 int k = 0; 1465 for (int p = pStart; p < pEnd; ++p) { 1466 for (int q = qStart; q < qEnd; ++q) { 1467 block[k] = visitor.visit(p, q, block[k]); 1468 ++k; 1469 } 1470 } 1471 ++blockIndex; 1472 } 1473 } 1474 return visitor.end(); 1475 } 1476 1477 /** {@inheritDoc} */ 1478 @Override 1479 public double walkInOptimizedOrder(final RealMatrixPreservingVisitor visitor) { 1480 visitor.start(rows, columns, 0, rows - 1, 0, columns - 1); 1481 int blockIndex = 0; 1482 for (int iBlock = 0; iBlock < blockRows; ++iBlock) { 1483 final int pStart = iBlock * BLOCK_SIZE; 1484 final int pEnd = FastMath.min(pStart + BLOCK_SIZE, rows); 1485 for (int jBlock = 0; jBlock < blockColumns; ++jBlock) { 1486 final int qStart = jBlock * BLOCK_SIZE; 1487 final int qEnd = FastMath.min(qStart + BLOCK_SIZE, columns); 1488 final double[] block = blocks[blockIndex]; 1489 int k = 0; 1490 for (int p = pStart; p < pEnd; ++p) { 1491 for (int q = qStart; q < qEnd; ++q) { 1492 visitor.visit(p, q, block[k]); 1493 ++k; 1494 } 1495 } 1496 ++blockIndex; 1497 } 1498 } 1499 return visitor.end(); 1500 } 1501 1502 /** {@inheritDoc} */ 1503 @Override 1504 public double walkInOptimizedOrder(final RealMatrixChangingVisitor visitor, 1505 final int startRow, final int endRow, 1506 final int startColumn, 1507 final int endColumn) 1508 throws OutOfRangeException, NumberIsTooSmallException { 1509 MatrixUtils.checkSubMatrixIndex(this, startRow, endRow, startColumn, endColumn); 1510 visitor.start(rows, columns, startRow, endRow, startColumn, endColumn); 1511 for (int iBlock = startRow / BLOCK_SIZE; iBlock < 1 + endRow / BLOCK_SIZE; ++iBlock) { 1512 final int p0 = iBlock * BLOCK_SIZE; 1513 final int pStart = FastMath.max(startRow, p0); 1514 final int pEnd = FastMath.min((iBlock + 1) * BLOCK_SIZE, 1 + endRow); 1515 for (int jBlock = startColumn / BLOCK_SIZE; jBlock < 1 + endColumn / BLOCK_SIZE; ++jBlock) { 1516 final int jWidth = blockWidth(jBlock); 1517 final int q0 = jBlock * BLOCK_SIZE; 1518 final int qStart = FastMath.max(startColumn, q0); 1519 final int qEnd = FastMath.min((jBlock + 1) * BLOCK_SIZE, 1 + endColumn); 1520 final double[] block = blocks[iBlock * blockColumns + jBlock]; 1521 for (int p = pStart; p < pEnd; ++p) { 1522 int k = (p - p0) * jWidth + qStart - q0; 1523 for (int q = qStart; q < qEnd; ++q) { 1524 block[k] = visitor.visit(p, q, block[k]); 1525 ++k; 1526 } 1527 } 1528 } 1529 } 1530 return visitor.end(); 1531 } 1532 1533 /** {@inheritDoc} */ 1534 @Override 1535 public double walkInOptimizedOrder(final RealMatrixPreservingVisitor visitor, 1536 final int startRow, final int endRow, 1537 final int startColumn, 1538 final int endColumn) 1539 throws OutOfRangeException, NumberIsTooSmallException { 1540 MatrixUtils.checkSubMatrixIndex(this, startRow, endRow, startColumn, endColumn); 1541 visitor.start(rows, columns, startRow, endRow, startColumn, endColumn); 1542 for (int iBlock = startRow / BLOCK_SIZE; iBlock < 1 + endRow / BLOCK_SIZE; ++iBlock) { 1543 final int p0 = iBlock * BLOCK_SIZE; 1544 final int pStart = FastMath.max(startRow, p0); 1545 final int pEnd = FastMath.min((iBlock + 1) * BLOCK_SIZE, 1 + endRow); 1546 for (int jBlock = startColumn / BLOCK_SIZE; jBlock < 1 + endColumn / BLOCK_SIZE; ++jBlock) { 1547 final int jWidth = blockWidth(jBlock); 1548 final int q0 = jBlock * BLOCK_SIZE; 1549 final int qStart = FastMath.max(startColumn, q0); 1550 final int qEnd = FastMath.min((jBlock + 1) * BLOCK_SIZE, 1 + endColumn); 1551 final double[] block = blocks[iBlock * blockColumns + jBlock]; 1552 for (int p = pStart; p < pEnd; ++p) { 1553 int k = (p - p0) * jWidth + qStart - q0; 1554 for (int q = qStart; q < qEnd; ++q) { 1555 visitor.visit(p, q, block[k]); 1556 ++k; 1557 } 1558 } 1559 } 1560 } 1561 return visitor.end(); 1562 } 1563 1564 /** 1565 * Get the height of a block. 1566 * @param blockRow row index (in block sense) of the block 1567 * @return height (number of rows) of the block 1568 */ 1569 private int blockHeight(final int blockRow) { 1570 return (blockRow == blockRows - 1) ? rows - blockRow * BLOCK_SIZE : BLOCK_SIZE; 1571 } 1572 1573 /** 1574 * Get the width of a block. 1575 * @param blockColumn column index (in block sense) of the block 1576 * @return width (number of columns) of the block 1577 */ 1578 private int blockWidth(final int blockColumn) { 1579 return (blockColumn == blockColumns - 1) ? columns - blockColumn * BLOCK_SIZE : BLOCK_SIZE; 1580 } 1581}