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