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