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.MathRuntimeException; 23 import org.apache.commons.math.linear.MatrixVisitorException; 24 import org.apache.commons.math.exception.util.LocalizedFormats; 25 26 /** 27 * Implementation of RealMatrix using a double[][] array to store entries and 28 * <a href="http://www.math.gatech.edu/~bourbaki/math2601/Web-notes/2num.pdf"> 29 * LU decomposition</a> to support linear system 30 * solution and inverse. 31 * <p> 32 * The LU decomposition is performed as needed, to support the following operations: <ul> 33 * <li>solve</li> 34 * <li>isSingular</li> 35 * <li>getDeterminant</li> 36 * <li>inverse</li> </ul></p> 37 * <p> 38 * <strong>Usage notes</strong>:<br> 39 * <ul><li> 40 * The LU decomposition is cached and reused on subsequent calls. 41 * If data are modified via references to the underlying array obtained using 42 * <code>getDataRef()</code>, then the stored LU decomposition will not be 43 * discarded. In this case, you need to explicitly invoke 44 * <code>LUDecompose()</code> to recompute the decomposition 45 * before using any of the methods above.</li> 46 * <li> 47 * As specified in the {@link RealMatrix} interface, matrix element indexing 48 * is 0-based -- e.g., <code>getEntry(0, 0)</code> 49 * returns the element in the first row, first column of the matrix.</li></ul> 50 * </p> 51 * 52 * @version $Revision: 1073158 $ $Date: 2011-02-21 22:46:52 +0100 (lun. 21 fvr. 2011) $ 53 */ 54 public class Array2DRowRealMatrix extends AbstractRealMatrix implements Serializable { 55 56 /** Serializable version identifier */ 57 private static final long serialVersionUID = -1067294169172445528L; 58 59 /** Entries of the matrix */ 60 protected double data[][]; 61 62 /** 63 * Creates a matrix with no data 64 */ 65 public Array2DRowRealMatrix() { 66 } 67 68 /** 69 * Create a new RealMatrix with the supplied row and column dimensions. 70 * 71 * @param rowDimension the number of rows in the new matrix 72 * @param columnDimension the number of columns in the new matrix 73 * @throws IllegalArgumentException if row or column dimension is not 74 * positive 75 */ 76 public Array2DRowRealMatrix(final int rowDimension, final int columnDimension) 77 throws IllegalArgumentException { 78 super(rowDimension, columnDimension); 79 data = new double[rowDimension][columnDimension]; 80 } 81 82 /** 83 * Create a new RealMatrix using the input array as the underlying 84 * data array. 85 * <p>The input array is copied, not referenced. This constructor has 86 * the same effect as calling {@link #Array2DRowRealMatrix(double[][], boolean)} 87 * with the second argument set to <code>true</code>.</p> 88 * 89 * @param d data for new matrix 90 * @throws IllegalArgumentException if <code>d</code> is not rectangular 91 * (not all rows have the same length) or empty 92 * @throws NullPointerException if <code>d</code> is null 93 * @see #Array2DRowRealMatrix(double[][], boolean) 94 */ 95 public Array2DRowRealMatrix(final double[][] d) 96 throws IllegalArgumentException, NullPointerException { 97 copyIn(d); 98 } 99 100 /** 101 * Create a new RealMatrix using the input array as the underlying 102 * data array. 103 * <p>If an array is built specially in order to be embedded in a 104 * RealMatrix and not used directly, the <code>copyArray</code> may be 105 * set to <code>false</code. This will prevent the copying and improve 106 * performance as no new array will be built and no data will be copied.</p> 107 * @param d data for new matrix 108 * @param copyArray if true, the input array will be copied, otherwise 109 * it will be referenced 110 * @throws IllegalArgumentException if <code>d</code> is not rectangular 111 * (not all rows have the same length) or empty 112 * @throws NullPointerException if <code>d</code> is null 113 * @see #Array2DRowRealMatrix(double[][]) 114 */ 115 public Array2DRowRealMatrix(final double[][] d, final boolean copyArray) 116 throws IllegalArgumentException, NullPointerException { 117 if (copyArray) { 118 copyIn(d); 119 } else { 120 if (d == null) { 121 throw new NullPointerException(); 122 } 123 final int nRows = d.length; 124 if (nRows == 0) { 125 throw MathRuntimeException.createIllegalArgumentException( 126 LocalizedFormats.AT_LEAST_ONE_ROW); 127 } 128 final int nCols = d[0].length; 129 if (nCols == 0) { 130 throw MathRuntimeException.createIllegalArgumentException( 131 LocalizedFormats.AT_LEAST_ONE_COLUMN); 132 } 133 for (int r = 1; r < nRows; r++) { 134 if (d[r].length != nCols) { 135 throw MathRuntimeException.createIllegalArgumentException( 136 LocalizedFormats.DIFFERENT_ROWS_LENGTHS, nCols, d[r].length); 137 } 138 } 139 data = d; 140 } 141 } 142 143 /** 144 * Create a new (column) RealMatrix using <code>v</code> as the 145 * data for the unique column of the <code>v.length x 1</code> matrix 146 * created. 147 * <p>The input array is copied, not referenced.</p> 148 * 149 * @param v column vector holding data for new matrix 150 */ 151 public Array2DRowRealMatrix(final double[] v) { 152 final int nRows = v.length; 153 data = new double[nRows][1]; 154 for (int row = 0; row < nRows; row++) { 155 data[row][0] = v[row]; 156 } 157 } 158 159 /** {@inheritDoc} */ 160 @Override 161 public RealMatrix createMatrix(final int rowDimension, final int columnDimension) 162 throws IllegalArgumentException { 163 return new Array2DRowRealMatrix(rowDimension, columnDimension); 164 } 165 166 /** {@inheritDoc} */ 167 @Override 168 public RealMatrix copy() { 169 return new Array2DRowRealMatrix(copyOut(), false); 170 } 171 172 /** {@inheritDoc} */ 173 @Override 174 public RealMatrix add(final RealMatrix m) 175 throws IllegalArgumentException { 176 try { 177 return add((Array2DRowRealMatrix) m); 178 } catch (ClassCastException cce) { 179 return super.add(m); 180 } 181 } 182 183 /** 184 * Compute the sum of this and <code>m</code>. 185 * 186 * @param m matrix to be added 187 * @return this + m 188 * @throws IllegalArgumentException if m is not the same size as this 189 */ 190 public Array2DRowRealMatrix add(final Array2DRowRealMatrix m) 191 throws IllegalArgumentException { 192 193 // safety check 194 MatrixUtils.checkAdditionCompatible(this, m); 195 196 final int rowCount = getRowDimension(); 197 final int columnCount = getColumnDimension(); 198 final double[][] outData = new double[rowCount][columnCount]; 199 for (int row = 0; row < rowCount; row++) { 200 final double[] dataRow = data[row]; 201 final double[] mRow = m.data[row]; 202 final double[] outDataRow = outData[row]; 203 for (int col = 0; col < columnCount; col++) { 204 outDataRow[col] = dataRow[col] + mRow[col]; 205 } 206 } 207 208 return new Array2DRowRealMatrix(outData, false); 209 210 } 211 212 /** {@inheritDoc} */ 213 @Override 214 public RealMatrix subtract(final RealMatrix m) 215 throws IllegalArgumentException { 216 try { 217 return subtract((Array2DRowRealMatrix) m); 218 } catch (ClassCastException cce) { 219 return super.subtract(m); 220 } 221 } 222 223 /** 224 * Compute this minus <code>m</code>. 225 * 226 * @param m matrix to be subtracted 227 * @return this + m 228 * @throws IllegalArgumentException if m is not the same size as this 229 */ 230 public Array2DRowRealMatrix subtract(final Array2DRowRealMatrix m) 231 throws IllegalArgumentException { 232 233 // safety check 234 MatrixUtils.checkSubtractionCompatible(this, m); 235 236 final int rowCount = getRowDimension(); 237 final int columnCount = getColumnDimension(); 238 final double[][] outData = new double[rowCount][columnCount]; 239 for (int row = 0; row < rowCount; row++) { 240 final double[] dataRow = data[row]; 241 final double[] mRow = m.data[row]; 242 final double[] outDataRow = outData[row]; 243 for (int col = 0; col < columnCount; col++) { 244 outDataRow[col] = dataRow[col] - mRow[col]; 245 } 246 } 247 248 return new Array2DRowRealMatrix(outData, false); 249 250 } 251 252 /** {@inheritDoc} */ 253 @Override 254 public RealMatrix multiply(final RealMatrix m) 255 throws IllegalArgumentException { 256 try { 257 return multiply((Array2DRowRealMatrix) m); 258 } catch (ClassCastException cce) { 259 return super.multiply(m); 260 } 261 } 262 263 /** 264 * Returns the result of postmultiplying this by <code>m</code>. 265 * @param m matrix to postmultiply by 266 * @return this*m 267 * @throws IllegalArgumentException 268 * if columnDimension(this) != rowDimension(m) 269 */ 270 public Array2DRowRealMatrix multiply(final Array2DRowRealMatrix m) 271 throws IllegalArgumentException { 272 273 // safety check 274 MatrixUtils.checkMultiplicationCompatible(this, m); 275 276 final int nRows = this.getRowDimension(); 277 final int nCols = m.getColumnDimension(); 278 final int nSum = this.getColumnDimension(); 279 final double[][] outData = new double[nRows][nCols]; 280 for (int row = 0; row < nRows; row++) { 281 final double[] dataRow = data[row]; 282 final double[] outDataRow = outData[row]; 283 for (int col = 0; col < nCols; col++) { 284 double sum = 0; 285 for (int i = 0; i < nSum; i++) { 286 sum += dataRow[i] * m.data[i][col]; 287 } 288 outDataRow[col] = sum; 289 } 290 } 291 292 return new Array2DRowRealMatrix(outData, false); 293 294 } 295 296 /** {@inheritDoc} */ 297 @Override 298 public double[][] getData() { 299 return copyOut(); 300 } 301 302 /** 303 * Returns a reference to the underlying data array. 304 * <p> 305 * Does <strong>not</strong> make a fresh copy of the underlying data.</p> 306 * 307 * @return 2-dimensional array of entries 308 */ 309 public double[][] getDataRef() { 310 return data; 311 } 312 313 /** {@inheritDoc} */ 314 @Override 315 public void setSubMatrix(final double[][] subMatrix, final int row, final int column) 316 throws MatrixIndexException { 317 if (data == null) { 318 if (row > 0) { 319 throw MathRuntimeException.createIllegalStateException( 320 LocalizedFormats.FIRST_ROWS_NOT_INITIALIZED_YET, row); 321 } 322 if (column > 0) { 323 throw MathRuntimeException.createIllegalStateException( 324 LocalizedFormats.FIRST_COLUMNS_NOT_INITIALIZED_YET, column); 325 } 326 final int nRows = subMatrix.length; 327 if (nRows == 0) { 328 throw MathRuntimeException.createIllegalArgumentException( 329 LocalizedFormats.AT_LEAST_ONE_ROW); 330 } 331 332 final int nCols = subMatrix[0].length; 333 if (nCols == 0) { 334 throw MathRuntimeException.createIllegalArgumentException( 335 LocalizedFormats.AT_LEAST_ONE_COLUMN); 336 } 337 data = new double[subMatrix.length][nCols]; 338 for (int i = 0; i < data.length; ++i) { 339 if (subMatrix[i].length != nCols) { 340 throw MathRuntimeException.createIllegalArgumentException( 341 LocalizedFormats.DIFFERENT_ROWS_LENGTHS, nCols, subMatrix[i].length); 342 } 343 System.arraycopy(subMatrix[i], 0, data[i + row], column, nCols); 344 } 345 } else { 346 super.setSubMatrix(subMatrix, row, column); 347 } 348 349 } 350 351 /** {@inheritDoc} */ 352 @Override 353 public double getEntry(final int row, final int column) 354 throws MatrixIndexException { 355 try { 356 return data[row][column]; 357 } catch (ArrayIndexOutOfBoundsException e) { 358 throw new MatrixIndexException( 359 LocalizedFormats.NO_SUCH_MATRIX_ENTRY, row, column, getRowDimension(), getColumnDimension()); 360 } 361 } 362 363 /** {@inheritDoc} */ 364 @Override 365 public void setEntry(final int row, final int column, final double value) 366 throws MatrixIndexException { 367 try { 368 data[row][column] = value; 369 } catch (ArrayIndexOutOfBoundsException e) { 370 throw new MatrixIndexException( 371 LocalizedFormats.NO_SUCH_MATRIX_ENTRY, row, column, getRowDimension(), getColumnDimension()); 372 } 373 } 374 375 /** {@inheritDoc} */ 376 @Override 377 public void addToEntry(final int row, final int column, final double increment) 378 throws MatrixIndexException { 379 try { 380 data[row][column] += increment; 381 } catch (ArrayIndexOutOfBoundsException e) { 382 throw new MatrixIndexException( 383 LocalizedFormats.NO_SUCH_MATRIX_ENTRY, row, column, getRowDimension(), getColumnDimension()); 384 } 385 } 386 387 /** {@inheritDoc} */ 388 @Override 389 public void multiplyEntry(final int row, final int column, final double factor) 390 throws MatrixIndexException { 391 try { 392 data[row][column] *= factor; 393 } catch (ArrayIndexOutOfBoundsException e) { 394 throw new MatrixIndexException( 395 LocalizedFormats.NO_SUCH_MATRIX_ENTRY, row, column, getRowDimension(), getColumnDimension()); 396 } 397 } 398 399 /** {@inheritDoc} */ 400 @Override 401 public int getRowDimension() { 402 return (data == null) ? 0 : data.length; 403 } 404 405 /** {@inheritDoc} */ 406 @Override 407 public int getColumnDimension() { 408 return ((data == null) || (data[0] == null)) ? 0 : data[0].length; 409 } 410 411 /** {@inheritDoc} */ 412 @Override 413 public double[] operate(final double[] v) 414 throws IllegalArgumentException { 415 final int nRows = this.getRowDimension(); 416 final int nCols = this.getColumnDimension(); 417 if (v.length != nCols) { 418 throw MathRuntimeException.createIllegalArgumentException( 419 LocalizedFormats.VECTOR_LENGTH_MISMATCH, v.length, nCols); 420 } 421 final double[] out = new double[nRows]; 422 for (int row = 0; row < nRows; row++) { 423 final double[] dataRow = data[row]; 424 double sum = 0; 425 for (int i = 0; i < nCols; i++) { 426 sum += dataRow[i] * v[i]; 427 } 428 out[row] = sum; 429 } 430 return out; 431 } 432 433 /** {@inheritDoc} */ 434 @Override 435 public double[] preMultiply(final double[] v) 436 throws IllegalArgumentException { 437 438 final int nRows = getRowDimension(); 439 final int nCols = getColumnDimension(); 440 if (v.length != nRows) { 441 throw MathRuntimeException.createIllegalArgumentException( 442 LocalizedFormats.VECTOR_LENGTH_MISMATCH, v.length, nRows); 443 } 444 445 final double[] out = new double[nCols]; 446 for (int col = 0; col < nCols; ++col) { 447 double sum = 0; 448 for (int i = 0; i < nRows; ++i) { 449 sum += data[i][col] * v[i]; 450 } 451 out[col] = sum; 452 } 453 454 return out; 455 456 } 457 458 /** {@inheritDoc} */ 459 @Override 460 public double walkInRowOrder(final RealMatrixChangingVisitor visitor) 461 throws MatrixVisitorException { 462 final int rows = getRowDimension(); 463 final int columns = getColumnDimension(); 464 visitor.start(rows, columns, 0, rows - 1, 0, columns - 1); 465 for (int i = 0; i < rows; ++i) { 466 final double[] rowI = data[i]; 467 for (int j = 0; j < columns; ++j) { 468 rowI[j] = visitor.visit(i, j, rowI[j]); 469 } 470 } 471 return visitor.end(); 472 } 473 474 /** {@inheritDoc} */ 475 @Override 476 public double walkInRowOrder(final RealMatrixPreservingVisitor visitor) 477 throws MatrixVisitorException { 478 final int rows = getRowDimension(); 479 final int columns = getColumnDimension(); 480 visitor.start(rows, columns, 0, rows - 1, 0, columns - 1); 481 for (int i = 0; i < rows; ++i) { 482 final double[] rowI = data[i]; 483 for (int j = 0; j < columns; ++j) { 484 visitor.visit(i, j, rowI[j]); 485 } 486 } 487 return visitor.end(); 488 } 489 490 /** {@inheritDoc} */ 491 @Override 492 public double walkInRowOrder(final RealMatrixChangingVisitor visitor, 493 final int startRow, final int endRow, 494 final int startColumn, final int endColumn) 495 throws MatrixIndexException, MatrixVisitorException { 496 MatrixUtils.checkSubMatrixIndex(this, startRow, endRow, startColumn, endColumn); 497 visitor.start(getRowDimension(), getColumnDimension(), 498 startRow, endRow, startColumn, endColumn); 499 for (int i = startRow; i <= endRow; ++i) { 500 final double[] rowI = data[i]; 501 for (int j = startColumn; j <= endColumn; ++j) { 502 rowI[j] = visitor.visit(i, j, rowI[j]); 503 } 504 } 505 return visitor.end(); 506 } 507 508 /** {@inheritDoc} */ 509 @Override 510 public double walkInRowOrder(final RealMatrixPreservingVisitor visitor, 511 final int startRow, final int endRow, 512 final int startColumn, final int endColumn) 513 throws MatrixIndexException, MatrixVisitorException { 514 MatrixUtils.checkSubMatrixIndex(this, startRow, endRow, startColumn, endColumn); 515 visitor.start(getRowDimension(), getColumnDimension(), 516 startRow, endRow, startColumn, endColumn); 517 for (int i = startRow; i <= endRow; ++i) { 518 final double[] rowI = data[i]; 519 for (int j = startColumn; j <= endColumn; ++j) { 520 visitor.visit(i, j, rowI[j]); 521 } 522 } 523 return visitor.end(); 524 } 525 526 /** {@inheritDoc} */ 527 @Override 528 public double walkInColumnOrder(final RealMatrixChangingVisitor visitor) 529 throws MatrixVisitorException { 530 final int rows = getRowDimension(); 531 final int columns = getColumnDimension(); 532 visitor.start(rows, columns, 0, rows - 1, 0, columns - 1); 533 for (int j = 0; j < columns; ++j) { 534 for (int i = 0; i < rows; ++i) { 535 final double[] rowI = data[i]; 536 rowI[j] = visitor.visit(i, j, rowI[j]); 537 } 538 } 539 return visitor.end(); 540 } 541 542 /** {@inheritDoc} */ 543 @Override 544 public double walkInColumnOrder(final RealMatrixPreservingVisitor visitor) 545 throws MatrixVisitorException { 546 final int rows = getRowDimension(); 547 final int columns = getColumnDimension(); 548 visitor.start(rows, columns, 0, rows - 1, 0, columns - 1); 549 for (int j = 0; j < columns; ++j) { 550 for (int i = 0; i < rows; ++i) { 551 visitor.visit(i, j, data[i][j]); 552 } 553 } 554 return visitor.end(); 555 } 556 557 /** {@inheritDoc} */ 558 @Override 559 public double walkInColumnOrder(final RealMatrixChangingVisitor visitor, 560 final int startRow, final int endRow, 561 final int startColumn, final int endColumn) 562 throws MatrixIndexException, MatrixVisitorException { 563 MatrixUtils.checkSubMatrixIndex(this, startRow, endRow, startColumn, endColumn); 564 visitor.start(getRowDimension(), getColumnDimension(), 565 startRow, endRow, startColumn, endColumn); 566 for (int j = startColumn; j <= endColumn; ++j) { 567 for (int i = startRow; i <= endRow; ++i) { 568 final double[] rowI = data[i]; 569 rowI[j] = visitor.visit(i, j, rowI[j]); 570 } 571 } 572 return visitor.end(); 573 } 574 575 /** {@inheritDoc} */ 576 @Override 577 public double walkInColumnOrder(final RealMatrixPreservingVisitor visitor, 578 final int startRow, final int endRow, 579 final int startColumn, final int endColumn) 580 throws MatrixIndexException, MatrixVisitorException { 581 MatrixUtils.checkSubMatrixIndex(this, startRow, endRow, startColumn, endColumn); 582 visitor.start(getRowDimension(), getColumnDimension(), 583 startRow, endRow, startColumn, endColumn); 584 for (int j = startColumn; j <= endColumn; ++j) { 585 for (int i = startRow; i <= endRow; ++i) { 586 visitor.visit(i, j, data[i][j]); 587 } 588 } 589 return visitor.end(); 590 } 591 592 /** 593 * Returns a fresh copy of the underlying data array. 594 * 595 * @return a copy of the underlying data array. 596 */ 597 private double[][] copyOut() { 598 final int nRows = this.getRowDimension(); 599 final double[][] out = new double[nRows][this.getColumnDimension()]; 600 // can't copy 2-d array in one shot, otherwise get row references 601 for (int i = 0; i < nRows; i++) { 602 System.arraycopy(data[i], 0, out[i], 0, data[i].length); 603 } 604 return out; 605 } 606 607 /** 608 * Replaces data with a fresh copy of the input array. 609 * <p> 610 * Verifies that the input array is rectangular and non-empty.</p> 611 * 612 * @param in data to copy in 613 * @throws IllegalArgumentException if input array is empty or not 614 * rectangular 615 * @throws NullPointerException if input array is null 616 */ 617 private void copyIn(final double[][] in) { 618 setSubMatrix(in, 0, 0); 619 } 620 621 } 622