1 /* 2 * Copyright (C) 2012 Alberto Irurueta Carro (alberto@irurueta.com) 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.irurueta.algebra; 17 18 import com.irurueta.statistics.GaussianRandomizer; 19 import com.irurueta.statistics.UniformRandomizer; 20 21 import java.io.Serializable; 22 import java.util.Arrays; 23 import java.util.Objects; 24 import java.util.Random; 25 26 /** 27 * Defines a matrix of numerical data. 28 * Values of a matrix are stored inside an internal array of data. 29 */ 30 @SuppressWarnings("DuplicatedCode") 31 public class Matrix implements Serializable, Cloneable { 32 /** 33 * Constant defining the default order in which values are stored in a 34 * matrix. This can be useful when copying data to and from an array. 35 */ 36 public static final boolean DEFAULT_USE_COLUMN_ORDER = true; 37 38 /** 39 * Number of matrix rows. 40 */ 41 private int rows; 42 43 /** 44 * Number of matrix columns. 45 */ 46 private int columns; 47 48 /** 49 * Array containing data of matrix. Data is stored linearly in memory 50 * using column order. 51 */ 52 private double[] buffer; 53 54 /** 55 * Array used for indexing column start positions within buffer. This 56 * is used for faster access to matrix elements. 57 */ 58 private int[] columnIndex; 59 60 /** 61 * Constructor of this class. 62 * By default, when instantiating a matrix its values are undefined. 63 * 64 * @param rows Defines number of rows in matrix. 65 * @param columns Defines number of columns in matrix. 66 * @throws WrongSizeException Exception thrown if matrix must be 67 * empty with provided size. In other words, matrices must have at least 68 * one row and one column. Leaving number of rows or columns to zero will 69 * raise this exception. 70 */ 71 public Matrix(final int rows, final int columns) throws WrongSizeException { 72 internalResize(rows, columns); 73 } 74 75 /** 76 * Copy constructor. 77 * 78 * @param m matrix to copy from. 79 */ 80 public Matrix(final Matrix m) { 81 try { 82 internalResize(m.getRows(), m.getColumns()); 83 } catch (final WrongSizeException ignore) { 84 // never happens 85 } 86 87 System.arraycopy(m.getBuffer(), 0, buffer, 0, buffer.length); 88 } 89 90 /** 91 * Returns number of rows in matrix. 92 * 93 * @return Number of rows in matrix. 94 */ 95 public int getRows() { 96 return rows; 97 } 98 99 /** 100 * Returns number of columns in matrix. 101 * 102 * @return Number of columns in matrix. 103 */ 104 public int getColumns() { 105 return columns; 106 } 107 108 /** 109 * Obtains element in matrix located at position (row and column). 110 * Notice that row and column position are zero-indexed. 111 * 112 * @param row Row to be used for element location. 113 * @param column Column to be used for element location. 114 * @return Value of element in matrix located at provided position. 115 * @throws ArrayIndexOutOfBoundsException Exception raised if attempting 116 * to access a location that lies outside the boundaries of the internal 117 * array containing matrix data. Notice that internal data is stored in 118 * column order, hence, if row position exceeds the number of rows in the 119 * matrix, this exception might not be raised depending on column position, 120 * however, if the column value exceeds the number of columns, it will 121 * always raise the exception. 122 */ 123 public double getElementAt(final int row, final int column) { 124 return buffer[columnIndex[column] + row]; 125 } 126 127 /** 128 * Returns index within internal buffer corresponding to provided row 129 * and column positions. 130 * 131 * @param row row to be used for element location. 132 * @param column column to be used for element location. 133 * @return index within internal buffer. 134 */ 135 public int getIndex(final int row, final int column) { 136 return columnIndex[column] + row; 137 } 138 139 /** 140 * Obtains element in matrix located at provided index value using the 141 * order specified by DEFAULT_USE_COLUMN_ORDER. 142 * Note: an index indicates linear position within matrix, in layman terms, 143 * if using column order, then position (i, j), where i stands for row 144 * index and j for column index, becomes index = j * rows + 1. On the other 145 * hand, if using row order then index becomes index = i * columns + j. 146 * 147 * @param index Linear position. 148 * @return Value of element contained at provided position. 149 * @throws ArrayIndexOutOfBoundsException Exception raised if index lays 150 * outside valid values, which range from zero (inclusive) to 151 * rows * columns (exclusive) 152 */ 153 public double getElementAtIndex(final int index) { 154 return getElementAtIndex(index, DEFAULT_USE_COLUMN_ORDER); 155 } 156 157 /** 158 * Obtain element in matrix located at provided index value using provided 159 * order (either column or row order). 160 * 161 * @param index Linear position. 162 * @param isColumnOrder if true indicates that values are retrieved 163 * assuming that are stored in column order. 164 * @return Value of element contained at provided position. 165 * @throws ArrayIndexOutOfBoundsException Exception raised if index lays 166 * outside valid values, which range from zero (inclusive) to 167 * rows * columns (exclusive) 168 */ 169 public double getElementAtIndex(final int index, final boolean isColumnOrder) { 170 171 if (isColumnOrder) { 172 return buffer[index]; 173 } else { 174 final var row = index / columns; 175 final var column = index % columns; 176 return buffer[columnIndex[column] + row]; 177 } 178 } 179 180 /** 181 * Sets element in matrix located at provided position (row and column). 182 * 183 * @param row Row to be used for element location to be set. 184 * @param column Column to be used for element location to be set. 185 * @param value Value to be set at provided position. 186 * @throws ArrayIndexOutOfBoundsException Exception raised if attempting 187 * to access a location that lies outside the boundaries of the internal 188 * array containing matrix data. Notice that internal data is stored in 189 * column order, hence, if row position exceeds the number of rows in the 190 * matrix, this exception might not be raised depending on column position, 191 * however, if the column value exceeds the number of columns, it will 192 * always raise the exception. 193 */ 194 public void setElementAt(final int row, final int column, final double value) { 195 buffer[columnIndex[column] + row] = value; 196 } 197 198 /** 199 * Sets element in matrix located at provided index using the order 200 * specified by DEFAULT_USE_COLUMN_ORDER. 201 * Note: an index indicates linear position within matrix, in layman terms, 202 * if using column order, then position (i, j), where i stands for row 203 * index and j for column index, becomes index = j * rows + 1. On the other 204 * hand, if using row order then index becomes index = i * columns + j. 205 * 206 * @param index Linear position. 207 * @param value Value of element contained at provided position. 208 * @throws ArrayIndexOutOfBoundsException Exception raised if index lays 209 * outside valid values, which range from zero (inclusive) to 210 * rows * columns (exclusive) 211 */ 212 public void setElementAtIndex(final int index, final double value) { 213 setElementAtIndex(index, value, DEFAULT_USE_COLUMN_ORDER); 214 } 215 216 /** 217 * Sets element in matrix located at provided index using provided order 218 * (either column or row order). 219 * 220 * @param index Linear position. 221 * @param value Value of element to be set at provided position. 222 * @param isColumnOrder if true indicates that values are retrieved 223 * assuming that are stored in column order. 224 * @throws ArrayIndexOutOfBoundsException Exception raised if index lays 225 * outside valid values, which range from zero (inclusive) to 226 * rows * columns (exclusive). 227 */ 228 public void setElementAtIndex(final int index, final double value, final boolean isColumnOrder) { 229 if (isColumnOrder) { 230 buffer[index] = value; 231 } else { 232 final var row = index / columns; 233 final var column = index % columns; 234 buffer[columnIndex[column] + row] = value; 235 } 236 } 237 238 /** 239 * Returns a new matrix instance containing the same data as this instance. 240 * 241 * @return A copy of this matrix instance. 242 * @throws CloneNotSupportedException if clone fails. 243 */ 244 @Override 245 public Matrix clone() throws CloneNotSupportedException { 246 var out = (Matrix) super.clone(); 247 out.copyFrom(this); 248 return out; 249 } 250 251 /** 252 * Copies this matrix data into provided matrix. Provided output matrix will 253 * be resized if needed. 254 * 255 * @param output Destination matrix where data will be copied to. 256 * @throws NullPointerException Exception raised if provided output matrix 257 * is null. 258 */ 259 public void copyTo(final Matrix output) { 260 // reset output size if not equal, otherwise reuse buffer and column 261 // index 262 if (output.getRows() != rows || output.getColumns() != columns) { 263 // resets size and column index 264 try { 265 output.resize(rows, columns); 266 } catch (final WrongSizeException ignore) { 267 // never happens 268 } 269 } 270 // copies content 271 System.arraycopy(buffer, 0, output.buffer, 0, buffer.length); 272 } 273 274 /** 275 * Copies the contents of provided matrix into this instance. This instance 276 * will be resized if needed. 277 * 278 * @param input Input matrix where data will be copied from. 279 * @throws NullPointerException Exception raised if provided input matrix is 280 * null. 281 */ 282 public void copyFrom(final Matrix input) { 283 // reset size if not equal, otherwise reuse buffer and column index 284 if (input.getRows() != rows || input.getColumns() != columns) { 285 // resets size and column index 286 try { 287 resize(input.getRows(), input.getColumns()); 288 } catch (final WrongSizeException ignore) { 289 // never happens 290 } 291 } 292 // copies content 293 System.arraycopy(input.buffer, 0, buffer, 0, buffer.length); 294 } 295 296 /** 297 * Adds another matrix to this matrix instance and stores the result in 298 * provided result matrix. If provided result matrix doesn't have proper 299 * size, it will be resized. 300 * 301 * @param other Matrix to be added to current instance. 302 * @param result Matrix where result of summation is stored. 303 * @throws WrongSizeException Exception thrown if provided matrix to be 304 * added (i.e. other) does not have the same size as this matrix. 305 * @throws NullPointerException Exception raised if provided matrices are 306 * null. 307 */ 308 public void add(final Matrix other, final Matrix result) throws WrongSizeException { 309 if (other.getRows() != rows || other.getColumns() != columns) { 310 throw new WrongSizeException(); 311 } 312 313 // resize result if needed 314 if (result.getRows() != rows || result.getColumns() != columns) { 315 result.resize(rows, columns); 316 } 317 318 internalAdd(other, result); 319 } 320 321 /** 322 * Adds provided matrix to this instance and returns the result as a new 323 * matrix instance. 324 * 325 * @param other Matrix to be added. 326 * @return Returns a new matrix containing the sum of this matrix with 327 * provided matrix. 328 * @throws WrongSizeException Exception raised if provided matrix does 329 * not have the same size as this matrix. 330 * @throws NullPointerException Exception raised if provided matrix is null. 331 */ 332 public Matrix addAndReturnNew(final Matrix other) throws WrongSizeException { 333 if (other.getRows() != rows || other.getColumns() != columns) { 334 throw new WrongSizeException(); 335 } 336 337 final var out = new Matrix(rows, columns); 338 internalAdd(other, out); 339 return out; 340 } 341 342 /** 343 * Adds provided matrix to this instance. 344 * 345 * @param other Matrix to be added. 346 * @throws WrongSizeException Exception raised if provided matrix does 347 * not have the same size as this matrix. 348 * @throws NullPointerException Exception raised if provided matrix is null. 349 */ 350 public void add(final Matrix other) throws WrongSizeException { 351 if (other.getRows() != rows || other.getColumns() != columns) { 352 throw new WrongSizeException(); 353 } 354 355 internalAdd(other, this); 356 } 357 358 /** 359 * Subtracts another matrix from this matrix instance and stores the result 360 * in provided result matrix. If provided result matrix doesn't have proper 361 * size, it will be resized. 362 * 363 * @param other Matrix to be added to current instance. 364 * @param result Matrix where result of subtraction is stored. 365 * @throws WrongSizeException Exception thrown if provided matrix to be 366 * subtracted (i.e. other) does not have the same size as this matrix. 367 * @throws NullPointerException Exception raised if provided matrices are 368 * null. 369 */ 370 public void subtract(final Matrix other, final Matrix result) throws WrongSizeException { 371 if (other.getRows() != rows || other.getColumns() != columns) { 372 throw new WrongSizeException(); 373 } 374 375 // resize result if needed 376 if (result.getRows() != rows || result.getColumns() != columns) { 377 result.resize(rows, columns); 378 } 379 380 internalSubtract(other, result); 381 } 382 383 /** 384 * Subtracts provided matrix from this instance and returns the result as a 385 * new matrix instance. 386 * 387 * @param other Matrix to be subtracted from. 388 * @return Returns a new matrix containing the subtraction of provided 389 * matrix from this matrix. 390 * @throws WrongSizeException Exception raised if provided matrix does 391 * not have the same size as this matrix. 392 * @throws NullPointerException Exception raised if provided matrix is null. 393 */ 394 public Matrix subtractAndReturnNew(final Matrix other) throws WrongSizeException { 395 if (other.getRows() != rows || other.getColumns() != columns) { 396 throw new WrongSizeException(); 397 } 398 399 final var out = new Matrix(rows, columns); 400 internalSubtract(other, out); 401 return out; 402 } 403 404 /** 405 * Subtracts provided matrix from this instance. 406 * 407 * @param other Matrix to be subtracted from. 408 * @throws WrongSizeException Exception raised if provided matrix does 409 * not have the same size as this matrix. 410 * @throws NullPointerException Exception raised if provided matrix is null. 411 */ 412 public void subtract(final Matrix other) throws WrongSizeException { 413 if (other.getRows() != rows || other.getColumns() != columns) { 414 throw new WrongSizeException(); 415 } 416 417 internalSubtract(other, this); 418 } 419 420 /** 421 * Multiplies another matrix to this matrix instance and stores the result 422 * in provided result matrix. If provided result matrix doesn't have proper 423 * size, it will be resized. 424 * 425 * @param other Matrix to be multiplied to current instance. 426 * @param result Matrix where result of product is stored. 427 * @throws WrongSizeException Exception thrown when current and provided 428 * matrix (i.e. other) has incompatible size for product computation. 429 * @throws NullPointerException Exception raised if provided matrices are 430 * null. 431 */ 432 public void multiply(final Matrix other, final Matrix result) throws WrongSizeException { 433 if (columns != other.rows) { 434 throw new WrongSizeException(); 435 } 436 437 // resize result if needed 438 if (result.rows != rows || result.columns != other.columns) { 439 result.resize(rows, other.columns); 440 } 441 442 internalMultiply(other, result); 443 } 444 445 /** 446 * Multiplies this matrix with provided matrix and returns the result as 447 * a new instance. 448 * If this matrix m1 has size m x n and provided matrix m2 has size p x q, 449 * then n must be equal to p so that product m1 * m2 can be correctly 450 * computed obtaining a matrix of size m x q, otherwise an 451 * IllegalArgumentException will be raised. 452 * 453 * @param other Right operand of matrix product 454 * @return Matrix containing result of multiplication 455 * @throws WrongSizeException Exception thrown when current and 456 * provided matrices have incompatible sizes for product computation. 457 * @throws NullPointerException Exception thrown if provided matrix is null 458 */ 459 public Matrix multiplyAndReturnNew(final Matrix other) throws WrongSizeException { 460 461 if (columns != other.rows) { 462 throw new WrongSizeException(); 463 } 464 465 final var out = new Matrix(rows, other.columns); 466 internalMultiply(other, out); 467 return out; 468 } 469 470 /** 471 * Multiplies this matrix with provided matrix. 472 * If this matrix m1 has size m x n and provided matrix m2 has size p x q, 473 * then n must be equal to p so that product m1 * m2 can be correctly 474 * computed resizing this matrix to a new one having size m x q, otherwise 475 * an IllegalArgumentException will be raised. 476 * 477 * @param other Right operand of matrix product 478 * @throws WrongSizeException Exception thrown when current and 479 * provided matrices have incompatible sizes for product computation. 480 * @throws NullPointerException Exception thrown if provided matrix is null 481 */ 482 public void multiply(final Matrix other) throws WrongSizeException { 483 484 if (columns != other.rows) { 485 throw new WrongSizeException(); 486 } 487 488 // instantiate new buffer and column index 489 final var resultBuffer = new double[rows * other.columns]; 490 final var resultColumnIndex = new int[other.columns]; 491 var counter = 0; 492 for (int i = 0; i < other.columns; i++) { 493 resultColumnIndex[i] = counter; 494 counter += rows; 495 } 496 497 internalMultiply(other, resultBuffer, resultColumnIndex); 498 // update matrix data 499 columns = other.columns; 500 columnIndex = resultColumnIndex; 501 buffer = resultBuffer; 502 } 503 504 /** 505 * Computes the Kronecker product with provided matrix and stores the 506 * result in provided result matrix. If provided result matrix doesn't 507 * have proper size, it will be resized. 508 * 509 * @param other other matrix to be Kronecker multiplied to current matrix. 510 * @param result matrix where result will be stored. 511 */ 512 public void multiplyKronecker(final Matrix other, final Matrix result) { 513 // resize result if needed 514 final var resultRows = rows * other.rows; 515 final var resultCols = columns * other.columns; 516 if (result.rows != resultRows || result.columns != resultCols) { 517 try { 518 result.resize(resultRows, resultCols); 519 } catch (final WrongSizeException ignore) { 520 // never thrown 521 } 522 } 523 524 internalMultiplyKronecker(other, result); 525 } 526 527 /** 528 * Computes the Kronecker product with provided matrix and returns the 529 * result as a new instance. 530 * If this matrix m1 has size mxn and provided matrix m2 has size pxq, 531 * the resulting matrix will be m*pxn*q. 532 * 533 * @param other other matrix to be Kronecker multiplied to current matrix. 534 * @return matrix containing result of Kronecker multiplication. 535 */ 536 public Matrix multiplyKroneckerAndReturnNew(final Matrix other) { 537 Matrix out = null; 538 try { 539 out = new Matrix(rows * other.rows, columns * other.columns); 540 internalMultiplyKronecker(other, out); 541 } catch (final WrongSizeException ignore) { 542 // never thrown 543 } 544 return out; 545 } 546 547 /** 548 * Computes the Kronecker product of this matrix with provided matrix and 549 * updates this matrix with the result of the multiplication. 550 * If this matrix m1 has size mxn and provided matrix m2 has size pxq, 551 * the resulting matrix will be resized to m*pxn*q. 552 * 553 * @param other other matrix to be Kronecker multiplied to current matrix. 554 */ 555 public void multiplyKronecker(final Matrix other) { 556 557 // instantiate new buffer and column index 558 final var resultRows = rows * other.rows; 559 final var resultCols = columns * other.columns; 560 final var resultBuffer = new double[resultRows * resultCols]; 561 final var resultColumnIndex = new int[resultCols]; 562 var counter = 0; 563 for (int i = 0; i < resultCols; i++) { 564 resultColumnIndex[i] = counter; 565 counter += resultRows; 566 } 567 568 internalMultiplyKronecker(other, resultBuffer, resultColumnIndex); 569 // update matrix data 570 rows = resultRows; 571 columns = resultCols; 572 columnIndex = resultColumnIndex; 573 buffer = resultBuffer; 574 } 575 576 /** 577 * Computes product by scalar of this instance multiplying all its elements 578 * by provided scalar value and storing the results in provided result 579 * matrix. If provided result matrix doesn't have proper size, it will be 580 * automatically resized. 581 * 582 * @param scalar Scalar amount that current matrix will be multiplied by. 583 * @param result Matrix where result of operation is stored. 584 */ 585 private void multiplyByScalar(final double scalar, final Matrix result) { 586 final var length = rows * columns; 587 for (int i = 0; i < length; i++) { 588 result.buffer[i] = scalar * buffer[i]; 589 } 590 } 591 592 /** 593 * Computes product by scalar of this instance multiplying all its elements 594 * by provided scalar value and returning the result as a new instance. 595 * 596 * @param scalar Scalar amount that current matrix will be multiplied by. 597 * @return Returns a new matrix instance that contains result of product by 598 * scalar. 599 */ 600 public Matrix multiplyByScalarAndReturnNew(final double scalar) { 601 Matrix out = null; 602 try { 603 out = new Matrix(rows, columns); 604 multiplyByScalar(scalar, out); 605 } catch (final WrongSizeException ignore) { 606 // never happens 607 } 608 return out; 609 } 610 611 /** 612 * Computes product by scalar of this instance multiplying all its elements 613 * by provided scalar value and returning the result as a new instance. 614 * 615 * @param scalar Scalar amount that current matrix will be multiplied by. 616 */ 617 public void multiplyByScalar(final double scalar) { 618 multiplyByScalar(scalar, this); 619 } 620 621 /** 622 * Checks if provided object is a Matrix instance having exactly the same 623 * contents as this matrix instance. 624 * 625 * @param obj Object to be compared 626 * @return Returns true if both objects are considered to be equal. 627 */ 628 @Override 629 public boolean equals(final Object obj) { 630 if (obj == null) { 631 return false; 632 } 633 if (obj == this) { 634 return true; 635 } 636 if (!(obj instanceof Matrix other)) { 637 return false; 638 } 639 640 return equals(other); 641 } 642 643 /** 644 * Computes and returns hash code for this instance. Hash codes are almost 645 * unique values that are useful for fast classification and storage of 646 * objects in collections. 647 * 648 * @return Hash code 649 */ 650 @Override 651 public int hashCode() { 652 return Objects.hash(this.rows, this.columns, Arrays.hashCode(this.buffer), Arrays.hashCode(this.columnIndex)); 653 } 654 655 /** 656 * Checks if provided matrix has exactly the same contents as this matrix 657 * instance. 658 * 659 * @param other Matrix to be compared. 660 * @return Returns true if both objects are considered to be equal (same 661 * content and size) 662 */ 663 public boolean equals(final Matrix other) { 664 return equals(other, 0.0); 665 } 666 667 /** 668 * Checks if provided matrix has contents similar to this matrix by checking 669 * that all values have a maximum difference equal to provided threshold and 670 * same size. 671 * 672 * @param other Matrix to be compared 673 * @param threshold Maximum difference allowed between values on same 674 * position to determine that matrices are equal 675 * @return True if matrices are considered to be equal (almost equal content 676 * and same size) 677 */ 678 public boolean equals(final Matrix other, final double threshold) { 679 if (other == null) { 680 return false; 681 } 682 if (other.getRows() != rows) { 683 return false; 684 } 685 if (other.getColumns() != columns) { 686 return false; 687 } 688 689 // check contents 690 final var length = rows * columns; 691 for (int i = 0; i < length; i++) { 692 if (Math.abs(buffer[i] - other.buffer[i]) > threshold) { 693 return false; 694 } 695 } 696 return true; 697 } 698 699 /** 700 * Computes element by element product (i.e. Hadamard product) between 701 * current and provided (i.e. other) matrix, and stores the result in 702 * provided result matrix. 703 * 704 * @param other Instance that will be used for element by element product 705 * with current instance. 706 * @param result Matrix where result of operation is stored. 707 * @throws WrongSizeException Exception raised if attempting to perform 708 * element by element product on matrices of different size. 709 * @throws NullPointerException Exception raised if provided matrix is null 710 */ 711 public void elementByElementProduct(final Matrix other, final Matrix result) throws WrongSizeException { 712 if (other.getRows() != rows || other.getColumns() != columns) { 713 throw new WrongSizeException(); 714 } 715 716 // resize result if needed 717 if (result.getRows() != rows || result.getColumns() != columns) { 718 result.resize(rows, columns); 719 } 720 721 internalElementByElementProduct(other, result); 722 } 723 724 /** 725 * Computes element by element product (i.e. Hadamard product) between 726 * current and provided instances and returns the result as a new instance. 727 * Example: 728 * Having matrices 729 * [1, 2, 3] [10, 11, 12] 730 * m1 = [4, 5, 6] m2 = [13, 14, 15] 731 * [7, 8, 9] [16, 17, 18] 732 * Then their element by element product will be 733 * [10, 22, 36 ] 734 * m3 = [52, 70, 90 ] 735 * [112, 136, 162] 736 * <p> 737 * Note: Attempting to perform element product on matrices of different size 738 * will raise an IllegalArgumentException 739 * 740 * @param other Instance that will be used for element by element product 741 * with current instance. 742 * @return A new Matrix containing element by element product result 743 * @throws WrongSizeException Exception raised if attempting to 744 * perform element by element product on matrices of different size. 745 * @throws NullPointerException Exception raised if provided matrix is null 746 */ 747 public Matrix elementByElementProductAndReturnNew(final Matrix other) throws WrongSizeException { 748 if (other.getRows() != rows || other.getColumns() != columns) { 749 throw new WrongSizeException(); 750 } 751 752 final var out = new Matrix(rows, columns); 753 internalElementByElementProduct(other, out); 754 return out; 755 } 756 757 /** 758 * Computes element by element product (i.e. Hadamard product) between 759 * current and provided instances. 760 * Example: 761 * Having matrices 762 * [1, 2, 3] [10, 11, 12] 763 * m1 = [4, 5, 6] m2 = [13, 14, 15] 764 * [7, 8, 9] [16, 17, 18] 765 * Then their element by element product will be 766 * [10, 22, 36 ] 767 * m3 = [52, 70, 90 ] 768 * [112, 136, 162] 769 * <p> 770 * Note: Attempting to perform element product on matrices of different size 771 * will raise an IllegalArgumentException 772 * 773 * @param other Instance that will be used for element by element product 774 * with current instance. 775 * @throws WrongSizeException Exception raised if attempting to 776 * perform element by element product on matrices of different size. 777 * @throws NullPointerException Exception raised if provided matrix is null 778 */ 779 public void elementByElementProduct(final Matrix other) throws WrongSizeException { 780 if (other.getRows() != rows || other.getColumns() != columns) { 781 throw new WrongSizeException(); 782 } 783 784 internalElementByElementProduct(other, this); 785 } 786 787 /** 788 * Transposes current matrix and stores result in provided matrix. If 789 * provided matrix doesn't have proper size, it will be resized. 790 * 791 * @param result Instance where transposed matrix is stored. 792 */ 793 public void transpose(final Matrix result) { 794 // resize result if needed 795 if (result.getRows() != columns || result.getColumns() != rows) { 796 try { 797 result.resize(columns, rows); 798 } catch (final WrongSizeException ignore) { 799 // never happens 800 } 801 } 802 internalTranspose(result); 803 } 804 805 /** 806 * Transposes current matrix and returns result as a new instance. 807 * Transposition of a matrix is done by exchanging rows and columns, in 808 * Layman terms, given a matrix m1 with elements located at m1(i,j), where i 809 * is the row index and j is the column index, then it follows that its 810 * transposed matrix m2 has the following property m2(i,j) = m1(j, i). 811 * 812 * @return A new Matrix instance containing transposed matrix. 813 */ 814 public Matrix transposeAndReturnNew() { 815 Matrix out = null; 816 try { 817 out = new Matrix(columns, rows); 818 internalTranspose(out); 819 } catch (final WrongSizeException ignore) { 820 // never happens 821 } 822 return out; 823 } 824 825 /** 826 * Transposes current matrix. 827 * Transposition of a matrix is done by exchanging rows and columns, in 828 * Layman terms, given a matrix m1 with elements located at m1(i,j), where i 829 * is the row index and j is the column index, then it follows that its 830 * transposed matrix m2 has the following property m2(i,j) = m1(j, i). 831 */ 832 public void transpose() { 833 834 final var newBuffer = new double[rows * columns]; 835 final var newColumnIndex = new int[rows]; 836 var counter = 0; 837 for (var i = 0; i < rows; i++) { 838 newColumnIndex[i] = counter; 839 counter += columns; 840 } 841 internalTranspose(newBuffer, newColumnIndex); 842 843 // update matrix data 844 845 // swap rows and columns 846 final var tmp = rows; 847 rows = columns; 848 columns = tmp; 849 850 columnIndex = newColumnIndex; 851 buffer = newBuffer; 852 } 853 854 /** 855 * Sets the contents of this matrix to provided value in all of its elements 856 * 857 * @param initValue Value to be set on all the elements of this matrix. 858 */ 859 public void initialize(final double initValue) { 860 // initialize buffer array to provided value 861 Arrays.fill(buffer, initValue); 862 } 863 864 /** 865 * Resizes current instance by removing its contents and resizing it to 866 * provided size. 867 * 868 * @param rows Number of rows to be set 869 * @param columns Number of columns to be set 870 * @throws WrongSizeException Exception raised if either rows or 871 * columns is zero. 872 */ 873 public void resize(final int rows, final int columns) throws WrongSizeException { 874 internalResize(rows, columns); 875 } 876 877 /** 878 * Resets current instance by removing its contents, resizing it to provided 879 * size and setting all its elements to provided value. 880 * 881 * @param rows Number of rows to be set 882 * @param columns Number of columns to be set 883 * @param initValue Value to be set in all of its elements 884 * @throws WrongSizeException Exception raised if either rows or 885 * columns is zero. 886 */ 887 public void reset(final int rows, final int columns, final double initValue) throws WrongSizeException { 888 internalResize(rows, columns); 889 initialize(initValue); 890 } 891 892 /** 893 * Returns the contents of the matrix as an array of values using 894 * DEFAULT_USE_COLUMN_ORDER to pick elements. 895 * 896 * @return Contents of matrix as an array 897 */ 898 public double[] toArray() { 899 return toArray(DEFAULT_USE_COLUMN_ORDER); 900 } 901 902 /** 903 * Returns the contents of the matrix as an array of values using provided 904 * order to pick elements. 905 * 906 * @param isColumnOrder If true, picks elements from matrix using column 907 * order, otherwise row order is used. 908 * @return Contents of matrix as an array, 909 */ 910 public double[] toArray(final boolean isColumnOrder) { 911 final var length = rows * columns; 912 913 if (isColumnOrder) { 914 return Arrays.copyOf(buffer, length); 915 } else { 916 final var out = new double[length]; 917 double value; 918 var counter = 0; 919 920 for (var j = 0; j < rows; j++) { 921 for (var i = 0; i < columns; i++) { 922 value = buffer[columnIndex[i] + j]; 923 out[counter] = value; 924 counter++; 925 } 926 } 927 928 return out; 929 } 930 } 931 932 /** 933 * Copies the contents of the matrix to an array of values using column 934 * order. 935 * 936 * @param result array where values will be copied to. 937 * @throws WrongSizeException if provided result array does not have the 938 * same number of elements as the matrix (i.e. rows x columns). 939 */ 940 public void toArray(final double[] result) throws WrongSizeException { 941 toArray(result, DEFAULT_USE_COLUMN_ORDER); 942 } 943 944 /** 945 * Copies the contents of the matrix to an array of values using provided 946 * order to pick elements. 947 * 948 * @param result array where values will be copied to. 949 * @param isColumnOrder if true, picks elements from matrix using column 950 * order, otherwise row order is used. 951 * @throws WrongSizeException if provided result array does not have the 952 * same number of elements as the matrix (i.e. rows x columns). 953 */ 954 public void toArray(final double[] result, final boolean isColumnOrder) throws WrongSizeException { 955 if (result.length != buffer.length) { 956 throw new WrongSizeException("result array must be equal to rows x columns"); 957 } 958 959 if (isColumnOrder) { 960 System.arraycopy(buffer, 0, result, 0, buffer.length); 961 } else { 962 double value; 963 var counter = 0; 964 965 for (var j = 0; j < rows; j++) { 966 for (var i = 0; i < columns; i++) { 967 value = buffer[columnIndex[i] + j]; 968 result[counter] = value; 969 counter++; 970 } 971 } 972 } 973 } 974 975 /** 976 * Returns current matrix internal buffer of data. 977 * 978 * @return Internal buffer of data. 979 */ 980 public double[] getBuffer() { 981 return buffer; 982 } 983 984 /** 985 * Obtains a sub-matrix of current matrix instance. Sub-matrix is obtained by 986 * copying all elements contained within provided coordinates (both top-left 987 * and bottom-right points are included within sub-matrix). 988 * 989 * @param topLeftRow Top-left row index where sub-matrix starts. 990 * @param topLeftColumn Top-left column index where sub-matrix starts. 991 * @param bottomRightRow Bottom-right row index where sub-matrix ends. 992 * @param bottomRightColumn Bottom-right column index where sub-matrix ends. 993 * @param result Instance where sub-matrix data is stored. 994 * @throws IllegalArgumentException Exception raised whenever top-left or 995 * bottom-right corners lie outside current matrix instance, or if top-left 996 * corner is indeed located below or at right side of bottom-right corner. 997 * @throws NullPointerException If provided result matrix is null. 998 */ 999 public void getSubmatrix(final int topLeftRow, final int topLeftColumn, final int bottomRightRow, 1000 final int bottomRightColumn, final Matrix result) { 1001 if (topLeftRow < 0 || topLeftRow >= rows || topLeftColumn < 0 || topLeftColumn >= columns 1002 || bottomRightRow < 0 || bottomRightRow >= rows || bottomRightColumn < 0 || bottomRightColumn >= columns 1003 || topLeftRow > bottomRightRow || topLeftColumn > bottomRightColumn) { 1004 throw new IllegalArgumentException(); 1005 } 1006 1007 final var subRows = bottomRightRow - topLeftRow + 1; 1008 final var subCols = bottomRightColumn - topLeftColumn + 1; 1009 if (result.getRows() != subRows || result.getColumns() != subCols) { 1010 // resize result 1011 try { 1012 result.resize(subRows, subCols); 1013 } catch (final WrongSizeException ignore) { 1014 // never happens 1015 } 1016 } 1017 internalGetSubmatrix(topLeftRow, topLeftColumn, bottomRightRow, bottomRightColumn, result); 1018 } 1019 1020 /** 1021 * Obtains a sub-matrix of current matrix instance. Sub-matrix is obtained by 1022 * copying all elements contained within provided coordinates (both top-left 1023 * and bottom-right points are included within sub-matrix). 1024 * 1025 * @param topLeftRow Top-left row index where sub-matrix starts. 1026 * @param topLeftColumn Top-left column index where sub-matrix starts. 1027 * @param bottomRightRow Bottom-right row index where sub-matrix ends. 1028 * @param bottomRightColumn Bottom-right column index where sub-matrix ends. 1029 * @return A new instance containing selected sub-matrix. 1030 * @throws IllegalArgumentException Exception raised whenever top-left or 1031 * bottom-right corners lie outside current matrix instance, or if top-left 1032 * corner is indeed located belo or at right side of bottom-right corner. 1033 */ 1034 public Matrix getSubmatrix(final int topLeftRow, final int topLeftColumn, 1035 final int bottomRightRow, final int bottomRightColumn) { 1036 if (topLeftRow < 0 || topLeftRow >= rows || topLeftColumn < 0 || topLeftColumn >= columns 1037 || bottomRightRow < 0 || bottomRightRow >= rows || bottomRightColumn < 0 || bottomRightColumn >= columns 1038 || topLeftRow > bottomRightRow || topLeftColumn > bottomRightColumn) { 1039 throw new IllegalArgumentException(); 1040 } 1041 1042 Matrix out; 1043 try { 1044 out = new Matrix(bottomRightRow - topLeftRow + 1, bottomRightColumn - topLeftColumn + 1); 1045 } catch (final WrongSizeException e) { 1046 throw new IllegalArgumentException(e); 1047 } 1048 internalGetSubmatrix(topLeftRow, topLeftColumn, bottomRightRow, bottomRightColumn, out); 1049 return out; 1050 } 1051 1052 /** 1053 * Retrieves a sub-matrix of current matrix instance as an array of values 1054 * using column order and storing the result in provided array. 1055 * Sub-matrix is obtained by copying all elements contained within provided 1056 * coordinates (both top-left and bottom-right points are included within 1057 * sub-matrix). 1058 * 1059 * @param topLeftRow Top-left row index where sub-matrix starts 1060 * @param topLeftColumn Top-left column index where sub-matrix starts 1061 * @param bottomRightRow Bottom-right row index where sub-matrix ends 1062 * @param bottomRightColumn Bottom-right column index where sub-matrix ends. 1063 * @param array Array where sub-matrix data is stored. 1064 * @throws IllegalArgumentException Thrown if provided coordinates lie 1065 * outside of matrix boundaries or if top-left corner is at the bottom or 1066 * right side of bottom-right corner 1067 * @throws WrongSizeException thrown if length of provided array does not 1068 * match the number of elements to be extracted from this matrix 1069 */ 1070 public void getSubmatrixAsArray(final int topLeftRow, final int topLeftColumn, 1071 final int bottomRightRow, final int bottomRightColumn, 1072 final double[] array) throws WrongSizeException { 1073 if (topLeftRow < 0 || topLeftRow >= rows || topLeftColumn < 0 || topLeftColumn >= columns || bottomRightRow < 0 1074 || bottomRightRow >= rows || bottomRightColumn < 0 || bottomRightColumn >= columns 1075 || topLeftRow > bottomRightRow || topLeftColumn > bottomRightColumn) { 1076 throw new IllegalArgumentException(); 1077 } 1078 1079 final var length = (bottomRightRow - topLeftRow + 1) * (bottomRightColumn - topLeftColumn + 1); 1080 if (array.length != length) { 1081 throw new WrongSizeException(); 1082 } 1083 1084 getSubmatrixAsArray(topLeftRow, topLeftColumn, bottomRightRow, bottomRightColumn, DEFAULT_USE_COLUMN_ORDER, 1085 array); 1086 } 1087 1088 /** 1089 * Retrieves a sub-matrix of current matrix instance as an array of values 1090 * using provided column order and storing the result in provided array. 1091 * Sub-matrix is obtained by copying all elements contained within provided 1092 * coordinates (both top-left and bottom-right points are included within 1093 * sub-matrix). 1094 * 1095 * @param topLeftRow Top-left row index where sub-matrix starts 1096 * @param topLeftColumn Top-left column index where sub-matrix starts 1097 * @param bottomRightRow Bottom-right row index where sub-matrix ends 1098 * @param bottomRightColumn Bottom-right column index where sub-matrix ends. 1099 * @param isColumnOrder If true, picks elements from matrix using column 1100 * order, otherwise row order is used. 1101 * @param array Array where sub-matrix data is stored. 1102 * @throws IllegalArgumentException Exception raised whenever top-left or 1103 * bottom-right corners lie outside current matrix instance, or if top-left 1104 * corner is indeed located below or at right side of bottom-right corner. 1105 * @throws WrongSizeException If provided array doesn't have proper length, 1106 * which must be equal to the amount of elements in desired sub-matrix. 1107 */ 1108 public void getSubmatrixAsArray( 1109 final int topLeftRow, final int topLeftColumn, final int bottomRightRow, final int bottomRightColumn, 1110 final boolean isColumnOrder, final double[] array) throws WrongSizeException { 1111 if (topLeftRow < 0 || topLeftRow >= rows || topLeftColumn < 0 || topLeftColumn >= columns || bottomRightRow < 0 1112 || bottomRightRow >= rows || bottomRightColumn < 0 || bottomRightColumn >= columns 1113 || topLeftRow > bottomRightRow || topLeftColumn > bottomRightColumn) { 1114 throw new IllegalArgumentException(); 1115 } 1116 1117 final var length = (bottomRightRow - topLeftRow + 1) * (bottomRightColumn - topLeftColumn + 1); 1118 if (array.length != length) { 1119 throw new WrongSizeException(); 1120 } 1121 1122 internalGetSubmatrixAsArray(topLeftRow, topLeftColumn, bottomRightRow, bottomRightColumn, isColumnOrder, array); 1123 } 1124 1125 /** 1126 * Obtains sub-matrix of current matrix instance as an array of values using 1127 * DEFAULT_USE_COLUMN_ORDER. Array is obtained by copying all elements 1128 * contained within provided coordinates (both top-left 1129 * and bottom-right points are included within sub-matrix). 1130 * 1131 * @param topLeftRow Top-left row index where sub-matrix starts 1132 * @param topLeftColumn Top-left column index where sub-matrix starts 1133 * @param bottomRightRow Bottom-right row index where sub-matrix ends 1134 * @param bottomRightColumn Bottom-right column index where sub-matrix ends. 1135 * @return An array containing sub-matrix elements 1136 * @throws IllegalArgumentException Exception raised whenever top-left or 1137 * bottom-right corners lie outside current matrix instance, or if top-left 1138 * corner is indeed located belo or at right side of bottom-right corner. 1139 */ 1140 public double[] getSubmatrixAsArray( 1141 final int topLeftRow, final int topLeftColumn, final int bottomRightRow, final int bottomRightColumn) { 1142 return getSubmatrixAsArray(topLeftRow, topLeftColumn, bottomRightRow, bottomRightColumn, 1143 DEFAULT_USE_COLUMN_ORDER); 1144 } 1145 1146 /** 1147 * Obtains sub-matrix of current matrix instance as an array of values using 1148 * provided order. Array is obtained by copying all elements 1149 * contained within provided coordinates (both top-left 1150 * and bottom-right points are included within sub-matrix). 1151 * 1152 * @param topLeftRow Top-left row index where sub-matrix starts 1153 * @param topLeftColumn Top-left column index where sub-matrix starts 1154 * @param bottomRightRow Bottom-right row index where sub-matrix ends 1155 * @param bottomRightColumn Bottom-right column index where sub-matrix ends. 1156 * @param isColumnOrder If true, picks elements from matrix using column 1157 * order, otherwise row order is used. 1158 * @return An array containing sub-matrix elements 1159 * @throws IllegalArgumentException Exception raised whenever top-left or 1160 * bottom-right corners lie outside current matrix instance, or if top-left 1161 * corner is indeed located below or at right side of bottom-right corner. 1162 */ 1163 public double[] getSubmatrixAsArray( 1164 final int topLeftRow, final int topLeftColumn, final int bottomRightRow, final int bottomRightColumn, 1165 final boolean isColumnOrder) { 1166 if (topLeftRow < 0 || topLeftRow >= rows || topLeftColumn < 0 || topLeftColumn >= columns || bottomRightRow < 0 1167 || bottomRightRow >= rows || bottomRightColumn < 0 || bottomRightColumn >= columns 1168 || topLeftRow > bottomRightRow || topLeftColumn > bottomRightColumn) { 1169 throw new IllegalArgumentException(); 1170 } 1171 1172 final var length = (bottomRightRow - topLeftRow + 1) * (bottomRightColumn - topLeftColumn + 1); 1173 1174 final var out = new double[length]; 1175 internalGetSubmatrixAsArray(topLeftRow, topLeftColumn, bottomRightRow, bottomRightColumn, isColumnOrder, out); 1176 return out; 1177 } 1178 1179 /** 1180 * Copies elements from provided sub-matrix into this matrix at provided 1181 * location. 1182 * 1183 * @param topLeftRow Top-left row index where sub-matrix copy starts 1184 * @param topLeftColumn Top-left column index where sub-matrix copy starts 1185 * @param bottomRightRow Bottom-right row index where sub-matrix copy ends 1186 * @param bottomRightColumn Bottom-right column index where sub-matrix copy 1187 * ends. 1188 * @param submatrix Sub-matrix to be copied 1189 * @throws IllegalArgumentException Exception raised whenever top-left or 1190 * bottom-right corners lie outside current matrix instance, or if top-left 1191 * corner is indeed located below or at right side of bottom-right corner. 1192 */ 1193 public void setSubmatrix( 1194 final int topLeftRow, final int topLeftColumn, final int bottomRightRow, final int bottomRightColumn, 1195 final Matrix submatrix) { 1196 setSubmatrix(topLeftRow, topLeftColumn, bottomRightRow, bottomRightColumn, submatrix, 0, 1197 0, submatrix.getRows() - 1, 1198 submatrix.getColumns() - 1); 1199 } 1200 1201 /** 1202 * Copies elements from provided sub-matrix into this matrix at provided 1203 * location 1204 * 1205 * @param topLeftRow Top-left row index where sub-matrix copy starts 1206 * @param topLeftColumn Top-left column index where sub-matrix copy starts 1207 * @param bottomRightRow Bottom-right row index where sub-matrix copy ends 1208 * @param bottomRightColumn Bottom-right column index where sub-matrix copy 1209 * ends. 1210 * @param submatrix Sub-matrix to be copied 1211 * @param submatrixTopLeftRow Top-left row index of sub-matrix where copy 1212 * starts 1213 * @param submatrixTopLeftColumn Top-left column index of sub-matrix where 1214 * copy starts 1215 * @param submatrixBottomRightRow Bottom-right row index of sub-matrix where 1216 * copy ends 1217 * @param submatrixBottomRightColumn Bottom-right column index of sub-matrix 1218 * where copy ends 1219 * @throws IllegalArgumentException Exception raised whenever top-left or 1220 * bottom-right corners lie outside current or provided matrices, or if 1221 * top-left corners are indeed located below or at right side of 1222 * bottom-right corners. 1223 */ 1224 public void setSubmatrix( 1225 final int topLeftRow, final int topLeftColumn, final int bottomRightRow, final int bottomRightColumn, 1226 final Matrix submatrix, final int submatrixTopLeftRow, final int submatrixTopLeftColumn, 1227 final int submatrixBottomRightRow, final int submatrixBottomRightColumn) { 1228 if (topLeftRow < 0 || topLeftRow >= rows || topLeftColumn < 0 || topLeftColumn >= columns || bottomRightRow < 0 1229 || bottomRightRow >= rows || bottomRightColumn < 0 || bottomRightColumn >= columns 1230 || topLeftRow > bottomRightRow || topLeftColumn > bottomRightColumn) { 1231 throw new IllegalArgumentException(); 1232 } 1233 1234 if (submatrixTopLeftRow < 0 || submatrixTopLeftColumn < 0 || submatrixBottomRightRow < 0 1235 || submatrixBottomRightRow >= submatrix.rows || submatrixBottomRightColumn < 0 1236 || submatrixBottomRightColumn >= submatrix.columns || submatrixTopLeftRow > submatrixBottomRightRow 1237 || submatrixTopLeftColumn > submatrixBottomRightColumn) { 1238 throw new IllegalArgumentException(); 1239 } 1240 1241 final var matrixRows = bottomRightRow - topLeftRow + 1; 1242 final var matrixColumns = bottomRightColumn - topLeftColumn + 1; 1243 final var submatrixRows = submatrixBottomRightRow - submatrixTopLeftRow + 1; 1244 final var submatrixColumns = submatrixBottomRightColumn - submatrixTopLeftColumn + 1; 1245 if (matrixRows != submatrixRows || matrixColumns != submatrixColumns) { 1246 throw new IllegalArgumentException(); 1247 } 1248 1249 var j2 = submatrixTopLeftColumn; 1250 int destPos; 1251 int sourcePos; 1252 for (var j = topLeftColumn; j <= bottomRightColumn; j++) { 1253 destPos = columnIndex[j] + topLeftRow; 1254 sourcePos = submatrix.columnIndex[j2] + submatrixTopLeftRow; 1255 for (var i = topLeftRow; i <= bottomRightRow; i++) { 1256 // Lines below are equivalent to commented code 1257 buffer[destPos] = submatrix.buffer[sourcePos]; 1258 destPos++; 1259 sourcePos++; 1260 } 1261 j2++; 1262 } 1263 } 1264 1265 /** 1266 * Sets elements in provided region to provided value. 1267 * 1268 * @param topLeftRow Top-left row index of region (inclusive). 1269 * @param topLeftColumn Top-left column index of region (inclusive). 1270 * @param bottomRightRow Bottom-right row index of region (inclusive). 1271 * @param bottomRightColumn Bottom-right column index of region (inclusive). 1272 * @param value Value to be set. 1273 * @throws IllegalArgumentException Exception raised whenever top-left or 1274 * bottom-right corners lie outside current matrix instance, or if top-left 1275 * corner is indeed located below or at right side of bottom-right corner. 1276 */ 1277 public void setSubmatrix( 1278 int topLeftRow, int topLeftColumn, int bottomRightRow, int bottomRightColumn, double value) { 1279 if (topLeftRow < 0 || topLeftRow >= rows || topLeftColumn < 0 || topLeftColumn >= columns || bottomRightRow < 0 1280 || bottomRightRow >= rows || bottomRightColumn < 0 || bottomRightColumn >= columns 1281 || topLeftRow > bottomRightRow || topLeftColumn > bottomRightColumn) { 1282 throw new IllegalArgumentException(); 1283 } 1284 1285 for (var j = topLeftColumn; j <= bottomRightColumn; j++) { 1286 for (var i = topLeftRow; i <= bottomRightRow; i++) { 1287 buffer[columnIndex[j] + i] = value; 1288 } 1289 } 1290 } 1291 1292 /** 1293 * Copies elements from provided array into this matrix at provided 1294 * location. Elements in array are copied into this matrix considering 1295 * DEFAULT_USE_COLUMN_ORDER. 1296 * 1297 * @param topLeftRow Top-left row index where copy starts. 1298 * @param topLeftColumn Top-left column index where copy starts. 1299 * @param bottomRightRow Bottom-right row index where copy ends. 1300 * @param bottomRightColumn Bottom-right column index where copy 1301 * ends. 1302 * @param values Array to be copied. 1303 * @throws IllegalArgumentException Exception raised whenever top-left or 1304 * bottom-right corners lie outside current matrix instance, or if top-left 1305 * corner is indeed located below or at right side of bottom-right corner. 1306 */ 1307 public void setSubmatrix( 1308 final int topLeftRow, final int topLeftColumn, final int bottomRightRow, final int bottomRightColumn, 1309 final double[] values) { 1310 setSubmatrix(topLeftRow, topLeftColumn, bottomRightRow, bottomRightColumn, values, DEFAULT_USE_COLUMN_ORDER); 1311 } 1312 1313 /** 1314 * Copies elements from provided array into this matrix at provided 1315 * location. Elements in array are copied into this matrix following 1316 * provided order (either row or column order). 1317 * 1318 * @param topLeftRow Top-left row index where copy starts. 1319 * @param topLeftColumn Top-left column index where copy ends. 1320 * @param bottomRightRow Bottom-right row index where copy ends. 1321 * @param bottomRightColumn Bottom-right column index where copy ends. 1322 * @param values Array to be copied. 1323 * @param isColumnOrder If true values are copied consecutively from array 1324 * following column order on the destination matrix, otherwise row order is 1325 * used. 1326 * @throws IllegalArgumentException Exception raised whenever top-left or 1327 * bottom-right corners lie outside current matrix instance, or if top-left 1328 * corner is indeed located below or at right side of bottom-right corner. 1329 */ 1330 public void setSubmatrix( 1331 final int topLeftRow, final int topLeftColumn, final int bottomRightRow, final int bottomRightColumn, 1332 final double[] values, final boolean isColumnOrder) { 1333 setSubmatrix(topLeftRow, topLeftColumn, bottomRightRow, bottomRightColumn, values, 0, 1334 values.length - 1, isColumnOrder); 1335 } 1336 1337 /** 1338 * Copies elements from provided array into this matrix at provided 1339 * location. Elements in array are copied into this matrix considering 1340 * DEFAULT_USE_COLUMN_ORDER starting at provided location until end provided 1341 * position. 1342 * 1343 * @param topLeftRow Top-left row index where copy starts (inclusive). 1344 * @param topLeftColumn Top-left column index where copy starts (inclusive). 1345 * @param bottomRightRow Bottom-right row index where copy ends (inclusive). 1346 * @param bottomRightColumn Bottom-right column index where copy 1347 * ends. (inclusive) 1348 * @param values Array to be copied. 1349 * @param valuesStart Position where copy from array will start (inclusive). 1350 * @param valuesEnd Position where copy from array will finish (inclusive). 1351 * @throws IllegalArgumentException Exception raised whenever top-left or 1352 * bottom-right corners lie outside current matrix instance, or if top-left 1353 * corner is indeed located below or at right side of bottom-right corner, 1354 * or if valuesStart and valuesEnd lie outside valid array positions or 1355 * start is greater than end position. 1356 */ 1357 public void setSubmatrix( 1358 final int topLeftRow, final int topLeftColumn, final int bottomRightRow, final int bottomRightColumn, 1359 final double[] values, final int valuesStart, final int valuesEnd) { 1360 setSubmatrix(topLeftRow, topLeftColumn, bottomRightRow, bottomRightColumn, values, valuesStart, valuesEnd, 1361 DEFAULT_USE_COLUMN_ORDER); 1362 } 1363 1364 /** 1365 * Copies elements from provided array into this matrix at provided 1366 * location. Elements in array are copied into this matrix following 1367 * provided order (either row or column order) starting at provided location 1368 * until end provided position. 1369 * 1370 * @param topLeftRow Top-left row index where copy starts (inclusive) 1371 * @param topLeftColumn Top-left column index where copy starts (inclusive) 1372 * @param bottomRightRow Bottom-right row index where copy ends (inclusive) 1373 * @param bottomRightColumn Bottom-right column index where copy 1374 * ends. (inclusive) 1375 * @param values Array to be copied. 1376 * @param valuesStart Position where copy from array will start (inclusive). 1377 * @param valuesEnd Position where copy from array will finish (inclusive). 1378 * @param isColumnOrder If true values are copied consecutively from array 1379 * following column order on the destination matrix, otherwise row order is 1380 * used. 1381 * @throws IllegalArgumentException Exception raised whenever top-left or 1382 * bottom-right corners lie outside current matrix instance, or if top-left 1383 * corner is indeed located below or at right side of bottom-right corner, 1384 * or if valuesStart and valuesEnd lie outside valid array positions or 1385 * start is greater than end position. 1386 */ 1387 public void setSubmatrix( 1388 final int topLeftRow, final int topLeftColumn, final int bottomRightRow, final int bottomRightColumn, 1389 final double[] values, final int valuesStart, final int valuesEnd, final boolean isColumnOrder) { 1390 1391 if (topLeftRow < 0 || topLeftRow >= rows || topLeftColumn < 0 || topLeftColumn >= columns || bottomRightRow < 0 1392 || bottomRightRow >= rows || bottomRightColumn < 0 || bottomRightColumn >= columns 1393 || topLeftRow > bottomRightRow || topLeftColumn > bottomRightColumn) { 1394 throw new IllegalArgumentException(); 1395 } 1396 1397 if (valuesStart < 0 || valuesEnd < 0 || valuesEnd >= values.length || valuesStart > valuesEnd) { 1398 throw new IllegalArgumentException(); 1399 } 1400 1401 final var matrixRows = bottomRightRow - topLeftRow + 1; 1402 final var matrixColumns = bottomRightColumn - topLeftColumn + 1; 1403 final var matrixLength = matrixRows * matrixColumns; 1404 final var valuesLength = valuesEnd - valuesStart + 1; 1405 1406 if (matrixLength != valuesLength) { 1407 throw new IllegalArgumentException(); 1408 } 1409 1410 var counter = valuesStart; 1411 if (isColumnOrder) { 1412 int destPos; 1413 for (var j = topLeftColumn; j <= bottomRightColumn; j++) { 1414 destPos = columnIndex[j] + topLeftRow; 1415 for (var i = topLeftRow; i <= bottomRightRow; i++) { 1416 // Two Lines below are equivalent to: 1417 // buffer[columnIndex[j] + i] = values[counter] 1418 buffer[destPos] = values[counter]; 1419 destPos++; 1420 counter++; 1421 } 1422 } 1423 1424 } else { 1425 for (var i = topLeftRow; i <= bottomRightRow; i++) { 1426 for (var j = topLeftColumn; j <= bottomRightColumn; j++) { 1427 buffer[columnIndex[j] + i] = values[counter]; 1428 counter++; 1429 } 1430 } 1431 } 1432 } 1433 1434 /** 1435 * Sets values into provided matrix to make it an identity matrix (all 1436 * elements in the diagonal equal to one, and remaining elements to zero). 1437 * 1438 * @param m Matrix where identity values are set. 1439 */ 1440 public static void identity(final Matrix m) { 1441 m.initialize(0.0); 1442 1443 final var minSize = Math.min(m.rows, m.columns); 1444 for (var i = 0; i < minSize; i++) { 1445 m.buffer[m.columnIndex[i] + i] = 1.0; 1446 } 1447 } 1448 1449 /** 1450 * Creates and returns a new matrix instance having all the elements on 1451 * the diagonal equal to one and the remaining ones equal to zero. 1452 * 1453 * @param rows Number of rows of created instance 1454 * @param columns Number of columns of created instance 1455 * @return An identity matrix 1456 * @throws WrongSizeException Raised if either rows or columns is 1457 * equal to zero 1458 */ 1459 public static Matrix identity(final int rows, final int columns) throws WrongSizeException { 1460 final var out = new Matrix(rows, columns); 1461 identity(out); 1462 return out; 1463 } 1464 1465 /** 1466 * Fills provided matrix with random uniform values ranging from minValue to 1467 * maxValue. 1468 * 1469 * @param minValue Minimum value of uniform random generated values 1470 * @param maxValue Maximum value of uniform random generated values 1471 * @param random Random generator 1472 * @param result Matrix where random values are stored. 1473 * @throws IllegalArgumentException if minValue <= maxValue 1474 * @throws NullPointerException if either provided random or result 1475 * instances are null 1476 */ 1477 public static void fillWithUniformRandomValues( 1478 final double minValue, final double maxValue, final Random random, final Matrix result) { 1479 final var randomizer = new UniformRandomizer(random); 1480 1481 final var length = result.rows * result.columns; 1482 1483 for (int i = 0; i < length; i++) { 1484 result.buffer[i] = randomizer.nextDouble(minValue, maxValue); 1485 } 1486 } 1487 1488 /** 1489 * Fills provided matrix with random uniform values ranging from minValue to 1490 * maxValue. 1491 * 1492 * @param minValue Minimum value of uniform random generated values 1493 * @param maxValue Maximum value of uniform random generated values 1494 * @param result Matrix where random values are stored 1495 * @throws IllegalArgumentException if minValue <= maxValue 1496 * @throws NullPointerException if provided result matrix is null 1497 */ 1498 public static void fillWithUniformRandomValues(final double minValue, final double maxValue, final Matrix result) { 1499 fillWithUniformRandomValues(minValue, maxValue, new Random(), result); 1500 } 1501 1502 /** 1503 * Creates new matrix instance using provided size and containing uniformly 1504 * distributed random values with provided range. 1505 * 1506 * @param rows Number of rows of instantiated matrix 1507 * @param columns Number of columns of instantiated matrix 1508 * @param minValue Minimum value of uniform random generated values 1509 * @param maxValue Maximum value of uniform random generated values 1510 * @return A new matrix containing uniform random values 1511 * @throws WrongSizeException Exception thrown if either rows or 1512 * columns is zero, or if the minimum random value is greater or equal 1513 * than the maximum random value 1514 * @throws IllegalArgumentException if minValue <= maxValue 1515 */ 1516 public static Matrix createWithUniformRandomValues( 1517 final int rows, final int columns, final double minValue, final double maxValue) throws WrongSizeException { 1518 return createWithUniformRandomValues(rows, columns, minValue, maxValue, new Random()); 1519 } 1520 1521 /** 1522 * Creates new matrix instance using provided size and containing uniformly 1523 * distributed random values with provided range and using provided random 1524 * generator 1525 * 1526 * @param rows Number of rows of instantiated matrix 1527 * @param columns Number of columns of instantiated matrix 1528 * @param minValue Minimum value of uniform random generated values 1529 * @param maxValue Maximum value of uniform random generated values 1530 * @param random A random generator. 1531 * @return A new matrix containing uniform random values 1532 * @throws WrongSizeException Exception thrown if either rows or 1533 * columns is zero, or if the minimum random value is greater or equal 1534 * than the maximum random value 1535 * @throws IllegalArgumentException if minValue <= maxValue 1536 */ 1537 public static Matrix createWithUniformRandomValues( 1538 final int rows, final int columns, final double minValue, final double maxValue, final Random random) 1539 throws WrongSizeException { 1540 1541 final var out = new Matrix(rows, columns); 1542 fillWithUniformRandomValues(minValue, maxValue, random, out); 1543 return out; 1544 } 1545 1546 /** 1547 * Fills provided matrix with random Gaussian values with provided mean and 1548 * standard deviation. 1549 * 1550 * @param mean Mean value of generated random values 1551 * @param standardDeviation Standard deviation of generated random values 1552 * @param random Random generator 1553 * @param result Matrix where random values are stored 1554 * @throws IllegalArgumentException if standard deviation is negative or 1555 * zero 1556 * @throws NullPointerException if provided result matrix is null 1557 */ 1558 public static void fillWithGaussianRandomValues( 1559 final double mean, final double standardDeviation, final Random random, final Matrix result) { 1560 1561 final var randomizer = new GaussianRandomizer(random, mean, standardDeviation); 1562 1563 final var length = result.rows * result.columns; 1564 1565 for (var i = 0; i < length; i++) { 1566 result.buffer[i] = randomizer.nextDouble(); 1567 } 1568 } 1569 1570 /** 1571 * Fills provided matrix with random Gaussian values with provided mean and 1572 * standard deviation 1573 * 1574 * @param mean Mean value of generated random values 1575 * @param standardDeviation Standard deviation of generated random values 1576 * @param result Matrix where random values are stored 1577 * @throws IllegalArgumentException if standard deviation is negative or 1578 * zero 1579 * @throws NullPointerException if provided result matrix is null 1580 */ 1581 public static void fillWithGaussianRandomValues( 1582 final double mean, final double standardDeviation, final Matrix result) { 1583 fillWithGaussianRandomValues(mean, standardDeviation, new Random(), result); 1584 } 1585 1586 /** 1587 * Creates new matrix instance using provided size and containing 1588 * gaussian/normal distributed random values with provided median and 1589 * standard deviation. 1590 * 1591 * @param rows Number of rows of instantiated matrix 1592 * @param columns Number of columns of instantiated matrix 1593 * @param mean Mean value of gaussian random generated values 1594 * @param standardDeviation Standard deviation of gaussian random generated 1595 * values 1596 * @return A new matrix containing gaussian random values 1597 * @throws WrongSizeException Exception thrown if either rows or 1598 * columns is zero, or if the standard deviation is negative or zero. 1599 * @throws IllegalArgumentException thrown if provided standard deviation 1600 * is negative or zero. 1601 */ 1602 public static Matrix createWithGaussianRandomValues( 1603 final int rows, final int columns, final double mean, final double standardDeviation) 1604 throws WrongSizeException { 1605 return createWithGaussianRandomValues(rows, columns, mean, standardDeviation, new Random()); 1606 } 1607 1608 /** 1609 * Creates new matrix instance using provided size and containing 1610 * gaussian/normal distributed random values with provided median and 1611 * standard deviation and using provided random generator 1612 * 1613 * @param rows Number of rows of instantiated matrix 1614 * @param columns Number of columns of instantiated matrix 1615 * @param mean Mean value of gaussian random generated values 1616 * @param standardDeviation Standard deviation of gaussian random generated 1617 * values 1618 * @param random A random generator. 1619 * @return A new matrix containing gaussian random values 1620 * @throws WrongSizeException Exception thrown if either rows or 1621 * columns is zero, or if the standard deviation is negative or zero. 1622 * @throws IllegalArgumentException thrown if provided standard deviation 1623 * is negative or zero. 1624 */ 1625 public static Matrix createWithGaussianRandomValues( 1626 final int rows, final int columns, final double mean, final double standardDeviation, final Random random) 1627 throws WrongSizeException { 1628 1629 final var out = new Matrix(rows, columns); 1630 fillWithGaussianRandomValues(mean, standardDeviation, random, out); 1631 return out; 1632 } 1633 1634 /** 1635 * Makes provided result matrix a diagonal matrix containing provided 1636 * elements in the diagonal. Elements outside the diagonal will be set to 1637 * zero 1638 * 1639 * @param diagonal Array containing the elements to be set on the diagonal 1640 * @param result Matrix where values are stored 1641 * @throws NullPointerException Exception thrown if provided parameters are 1642 * null 1643 */ 1644 public static void diagonal(final double[] diagonal, final Matrix result) { 1645 1646 result.initialize(0.0); 1647 // set diagonal elements 1648 //noinspection ManualArrayCopy 1649 for (var i = 0; i < diagonal.length; i++) { 1650 result.buffer[result.columnIndex[i] + i] = diagonal[i]; 1651 } 1652 } 1653 1654 /** 1655 * Creates a diagonal matrix having all the elements in provided array in 1656 * its diagonal and the remaining elements equal to zero. 1657 * Returned matrix will have size n x n, where n is the length of the array 1658 * 1659 * @param diagonal Array containing the elements to be set on the diagonal 1660 * @return A diagonal matrix 1661 * @throws NullPointerException Raised if provided diagonal array is null 1662 */ 1663 public static Matrix diagonal(final double[] diagonal) { 1664 1665 Matrix out = null; 1666 try { 1667 out = new Matrix(diagonal.length, diagonal.length); 1668 diagonal(diagonal, out); 1669 } catch (final WrongSizeException ignore) { 1670 // never happens 1671 } 1672 1673 return out; 1674 } 1675 1676 /** 1677 * Instantiates new matrix from array using DEFAULT_USE_COLUMN_ORDER 1678 * 1679 * @param array Array used as source to copy values from 1680 * @return Returns matrix created from array 1681 */ 1682 public static Matrix newFromArray(final double[] array) { 1683 return newFromArray(array, DEFAULT_USE_COLUMN_ORDER); 1684 } 1685 1686 /** 1687 * Instantiates new matrix from array using either column or row order 1688 * 1689 * @param array Array used as source to copy values from 1690 * @param isColumnOrder True if column order must be used, false otherwise 1691 * @return Returns matrix created from array 1692 */ 1693 public static Matrix newFromArray(final double[] array, final boolean isColumnOrder) { 1694 Matrix m = null; 1695 try { 1696 if (isColumnOrder) { 1697 m = new Matrix(array.length, 1); 1698 m.setSubmatrix(0, 0, array.length - 1, 0, array); 1699 } else { 1700 m = new Matrix(1, array.length); 1701 m.setSubmatrix(0, 0, 0, array.length - 1, array); 1702 } 1703 } catch (final WrongSizeException ignore) { 1704 // never happens 1705 } 1706 return m; 1707 } 1708 1709 /** 1710 * Copies elements of array into this instance using column order. 1711 * 1712 * @param array array to copy values from. 1713 * @throws WrongSizeException if provided array length is not equal to the 1714 * number of rows multiplied per the number of columns of this instance. 1715 */ 1716 public void fromArray(final double[] array) throws WrongSizeException { 1717 fromArray(array, DEFAULT_USE_COLUMN_ORDER); 1718 } 1719 1720 /** 1721 * Copies elements of array into this instance using provided order. 1722 * 1723 * @param array array to copy values from. 1724 * @param isColumnOrder true to use column order, false otherwise. 1725 * @throws WrongSizeException if provided array length is not equal to the 1726 * number of rows multiplied per the number of columns of this instance. 1727 */ 1728 public void fromArray(final double[] array, final boolean isColumnOrder) throws WrongSizeException { 1729 if (array.length != buffer.length) { 1730 throw new WrongSizeException("array length must be equal to rows x columns"); 1731 } 1732 1733 if (isColumnOrder) { 1734 System.arraycopy(array, 0, buffer, 0, array.length); 1735 } else { 1736 var counter = 0; 1737 1738 for (var j = 0; j < rows; j++) { 1739 for (var i = 0; i < columns; i++) { 1740 buffer[columnIndex[i] + j] = array[counter]; 1741 counter++; 1742 } 1743 } 1744 } 1745 } 1746 1747 /** 1748 * Symmetrizes this instance and stores the result into provided instance. 1749 * Symmetrization is done by averaging this instance with its transpose 1750 * (i.e. S = (M+M')/2 1751 * 1752 * @param result instance where symmetrized version of this instance will be 1753 * stored. 1754 * @throws WrongSizeException if this instance is not square or provided 1755 * result instance doesn't have the same size as this instance. 1756 */ 1757 public void symmetrize(final Matrix result) throws WrongSizeException { 1758 if (rows != columns) { 1759 throw new WrongSizeException("matrix must be square"); 1760 } 1761 if (result.getRows() != rows || result.getColumns() != columns) { 1762 throw new WrongSizeException("result matrix must have the size of this instance"); 1763 } 1764 1765 // S = (M+M')/2 1766 double value1; 1767 double value2; 1768 double avg; 1769 int pos1; 1770 int pos2; 1771 for (var i = 0; i < columns; i++) { 1772 for (var j = i; j < rows; j++) { 1773 // value at (i, j) 1774 pos1 = columnIndex[i] + j; 1775 value1 = buffer[pos1]; 1776 // transposed value (i.e. value at (j,i)) 1777 pos2 = columnIndex[j] + i; 1778 value2 = buffer[pos2]; 1779 1780 avg = 0.5 * (value1 + value2); 1781 result.buffer[pos1] = avg; 1782 result.buffer[pos2] = avg; 1783 } 1784 } 1785 } 1786 1787 /** 1788 * Symmetrizes this instance and returns the result as a new matrix 1789 * instance. 1790 * Symmetrization is done by averaging this instance with its transpose 1791 * (i.e. S = (M+M')/2 1792 * 1793 * @return a new symmetrizes version of this instance. 1794 * @throws WrongSizeException if this instance is not square. 1795 */ 1796 public Matrix symmetrizeAndReturnNew() throws WrongSizeException { 1797 final var m = new Matrix(rows, columns); 1798 symmetrize(m); 1799 return m; 1800 } 1801 1802 /** 1803 * Symmetrizes this instance and updates it with computed value. 1804 * Symmetrization is done by averaging this instance with its transpose 1805 * (i.e. S = (M+M')/2 1806 * 1807 * @throws WrongSizeException if this instance is not square. 1808 */ 1809 public void symmetrize() throws WrongSizeException { 1810 symmetrize(this); 1811 } 1812 1813 /** 1814 * Method to internally add two matrices. 1815 * 1816 * @param other Matrix to be added to current matrix 1817 * @param result Matrix where result will be stored. 1818 */ 1819 private void internalAdd(final Matrix other, final Matrix result) { 1820 final var length = rows * columns; 1821 for (var i = 0; i < length; i++) { 1822 result.buffer[i] = buffer[i] + other.buffer[i]; 1823 } 1824 } 1825 1826 /** 1827 * Method to internally subtract two matrices. 1828 * 1829 * @param other Matrix to be subtracted from current matrix. 1830 * @param result Matrix where result will be stored. 1831 */ 1832 private void internalSubtract(final Matrix other, final Matrix result) { 1833 final var length = rows * columns; 1834 for (var i = 0; i < length; i++) { 1835 result.buffer[i] = buffer[i] - other.buffer[i]; 1836 } 1837 } 1838 1839 /** 1840 * Method to internally multiply two matrices. 1841 * 1842 * @param other Matrix to be multiplied to current matrix 1843 * @param resultBuffer Matrix buffer of data where result will be stored. 1844 * @param resultColumnIndex Array of matrix column indices where result will 1845 * be stored. 1846 */ 1847 private void internalMultiply( 1848 final Matrix other, final double[] resultBuffer, final int[] resultColumnIndex) { 1849 final var columns2 = other.columns; 1850 double value; 1851 for (var k = 0; k < columns2; k++) { 1852 for (var j = 0; j < rows; j++) { 1853 value = 0.0; 1854 for (var i = 0; i < columns; i++) { 1855 value += buffer[columnIndex[i] + j] * 1856 other.buffer[other.columnIndex[k] + i]; 1857 } 1858 resultBuffer[resultColumnIndex[k] + j] = value; 1859 } 1860 } 1861 } 1862 1863 /** 1864 * Method to internally multiply two matrices. 1865 * 1866 * @param other Matrix to be multiplied to current matrix. 1867 * @param result Matrix where result will be stored. 1868 */ 1869 private void internalMultiply(final Matrix other, final Matrix result) { 1870 internalMultiply(other, result.buffer, result.columnIndex); 1871 } 1872 1873 /** 1874 * Method to internally compute the Kronecker product between two matrices. 1875 * 1876 * @param other other matrix to be Kronecker multiplied to current matrix. 1877 * @param resultBuffer matrix buffer of data where result will be stored. 1878 * @param resultColumnIndex array of matrix column indices where result will 1879 * be stored. 1880 */ 1881 private void internalMultiplyKronecker( 1882 final Matrix other, final double[] resultBuffer, final int[] resultColumnIndex) { 1883 final var rows2 = other.rows; 1884 final var columns2 = other.columns; 1885 1886 for (var j1 = 0; j1 < rows; j1++) { 1887 final var startJ3 = j1 * other.rows; 1888 for (var i1 = 0; i1 < columns; i1++) { 1889 final var startI3 = i1 * other.columns; 1890 final var value1 = buffer[columnIndex[i1] + j1]; 1891 1892 for (var j2 = 0; j2 < rows2; j2++) { 1893 final var j3 = startJ3 + j2; 1894 for (var i2 = 0; i2 < columns2; i2++) { 1895 final var i3 = startI3 + i2; 1896 final var value2 = other.buffer[other.columnIndex[i2] + j2]; 1897 1898 final var value3 = value1 * value2; 1899 resultBuffer[resultColumnIndex[i3] + j3] = value3; 1900 } 1901 } 1902 } 1903 } 1904 } 1905 1906 /** 1907 * Method to internally compute the Kronecker product between two matrices. 1908 * 1909 * @param other other matrix to be Kronecker multiplied to current matrix. 1910 * @param result matrix where result will be stored. 1911 */ 1912 private void internalMultiplyKronecker(final Matrix other, final Matrix result) { 1913 internalMultiplyKronecker(other, result.buffer, result.columnIndex); 1914 } 1915 1916 /** 1917 * Method to internally compute element by element product of two matrices. 1918 * 1919 * @param other Matrix to be element by element multiplied to current matrix 1920 * @param result Matrix where result will be stored. 1921 */ 1922 private void internalElementByElementProduct(final Matrix other, final Matrix result) { 1923 final var length = rows * columns; 1924 for (var i = 0; i < length; i++) { 1925 result.buffer[i] = buffer[i] * other.buffer[i]; 1926 } 1927 } 1928 1929 /** 1930 * Method to internally compute matrix transposition. 1931 * 1932 * @param resultBuffer Buffer where transposed matrix data is stored. 1933 * @param resultColumnIndex Buffer where indices of transposed matrix data 1934 * is stored. 1935 */ 1936 private void internalTranspose(final double[] resultBuffer, final int[] resultColumnIndex) { 1937 for (var j = 0; j < rows; j++) { 1938 for (var i = 0; i < columns; i++) { 1939 resultBuffer[resultColumnIndex[j] + i] = buffer[columnIndex[i] + j]; 1940 } 1941 } 1942 } 1943 1944 /** 1945 * Method to internally compute matrix transposition. 1946 * 1947 * @param result Matrix where transposed data is stored. 1948 */ 1949 private void internalTranspose(final Matrix result) { 1950 internalTranspose(result.buffer, result.columnIndex); 1951 } 1952 1953 /** 1954 * Method used internally to remove matrix contents and resizing it. 1955 * 1956 * @param rows Number of rows to be set 1957 * @param columns Number of columns to be set. 1958 * @throws WrongSizeException Exception raised if either rows or 1959 * columns is zero. 1960 */ 1961 private void internalResize(final int rows, final int columns) throws WrongSizeException { 1962 if (rows == 0 || columns == 0) { 1963 throw new WrongSizeException(); 1964 } 1965 1966 this.rows = rows; 1967 this.columns = columns; 1968 1969 // instantiate buffers of data 1970 buffer = new double[rows * columns]; 1971 columnIndex = new int[columns]; 1972 1973 // initialize column index 1974 var counter = 0; 1975 for (var i = 0; i < columns; i++) { 1976 columnIndex[i] = counter; 1977 counter += rows; 1978 } 1979 } 1980 1981 /** 1982 * Internal method to retrieve a sub-matrix of current matrix instance. 1983 * Sub-matrix is obtained by copying all elements contained within provided 1984 * coordinates (both top-left and bottom-right points are included within 1985 * sub-matrix). 1986 * 1987 * @param topLeftRow Top-left row index where sub-matrix starts. 1988 * @param topLeftColumn Top-left column index where sub-matrix starts. 1989 * @param bottomRightRow Bottom-right row index where sub-matrix ends. 1990 * @param bottomRightColumn Bottom-right column index where sub-matrix ends. 1991 * @param result Instance where sub-matrix data is stored. 1992 */ 1993 private void internalGetSubmatrix( 1994 final int topLeftRow, final int topLeftColumn, final int bottomRightRow, final int bottomRightColumn, 1995 final Matrix result) { 1996 var i2 = 0; 1997 var j2 = 0; 1998 for (var j = topLeftColumn; j <= bottomRightColumn; j++) { 1999 for (var i = topLeftRow; i <= bottomRightRow; i++) { 2000 result.buffer[result.columnIndex[j2] + i2] = buffer[columnIndex[j] + i]; 2001 i2++; 2002 } 2003 i2 = 0; 2004 j2++; 2005 } 2006 } 2007 2008 /** 2009 * Internal method to retrieve a sub-matrix of current matrix instance as an 2010 * array of values using provided column order and storing the result in 2011 * provided array. 2012 * Sub-matrix is obtained by copying all elements contained within provided 2013 * coordinates (both top-left and bottom-right points are included within 2014 * sub-matrix). 2015 * 2016 * @param topLeftRow Top-left row index where sub-matrix starts 2017 * @param topLeftColumn Top-left column index where sub-matrix starts 2018 * @param bottomRightRow Bottom-right row index where sub-matrix ends 2019 * @param bottomRightColumn Bottom-right column index where sub-matrix ends. 2020 * @param isColumnOrder If true, picks elements from matrix using column 2021 * order, otherwise row order is used. 2022 * @param result Array where sub-matrix data is stored. 2023 */ 2024 private void internalGetSubmatrixAsArray( 2025 final int topLeftRow, final int topLeftColumn, final int bottomRightRow, 2026 final int bottomRightColumn, final boolean isColumnOrder, final double[] result) { 2027 var counter = 0; 2028 if (isColumnOrder) { 2029 for (var j = topLeftColumn; j <= bottomRightColumn; j++) { 2030 for (var i = topLeftRow; i <= bottomRightRow; i++) { 2031 result[counter] = buffer[columnIndex[j] + i]; 2032 counter++; 2033 } 2034 } 2035 } else { 2036 for (var i = topLeftRow; i <= bottomRightRow; i++) { 2037 for (var j = topLeftColumn; j <= bottomRightColumn; j++) { 2038 result[counter] = buffer[columnIndex[j] + i]; 2039 counter++; 2040 } 2041 } 2042 } 2043 } 2044 }