001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 018package org.apache.commons.math4.legacy.linear; 019 020import java.io.Serializable; 021 022import org.apache.commons.math4.legacy.core.Field; 023import org.apache.commons.math4.legacy.core.FieldElement; 024import org.apache.commons.math4.legacy.exception.DimensionMismatchException; 025import org.apache.commons.math4.legacy.exception.MathIllegalStateException; 026import org.apache.commons.math4.legacy.exception.NoDataException; 027import org.apache.commons.math4.legacy.exception.NotStrictlyPositiveException; 028import org.apache.commons.math4.legacy.exception.NullArgumentException; 029import org.apache.commons.math4.legacy.exception.NumberIsTooSmallException; 030import org.apache.commons.math4.legacy.exception.OutOfRangeException; 031import org.apache.commons.math4.legacy.exception.util.LocalizedFormats; 032import org.apache.commons.math4.legacy.core.MathArrays; 033 034/** 035 * Implementation of {@code FieldMatrix<T>} using a {@link FieldElement}[][] array to store entries. 036 * <p> 037 * As specified in the {@link FieldMatrix} interface, matrix element indexing 038 * is 0-based -- e.g., <code>getEntry(0, 0)</code> 039 * returns the element in the first row, first column of the matrix. 040 * </p> 041 * 042 * @param <T> the type of the field elements 043 */ 044public class Array2DRowFieldMatrix<T extends FieldElement<T>> 045 extends AbstractFieldMatrix<T> 046 implements Serializable { 047 /** Serializable version identifier. */ 048 private static final long serialVersionUID = 7260756672015356458L; 049 /** Entries of the matrix. */ 050 private T[][] data; 051 052 /** 053 * Creates a matrix with no data. 054 * @param field field to which the elements belong 055 */ 056 public Array2DRowFieldMatrix(final Field<T> field) { 057 super(field); 058 } 059 060 /** 061 * Create a new {@code FieldMatrix<T>} with the supplied row and column dimensions. 062 * 063 * @param field Field to which the elements belong. 064 * @param rowDimension Number of rows in the new matrix. 065 * @param columnDimension Number of columns in the new matrix. 066 * @throws NotStrictlyPositiveException if row or column dimension is not positive. 067 */ 068 public Array2DRowFieldMatrix(final Field<T> field, final int rowDimension, 069 final int columnDimension) 070 throws NotStrictlyPositiveException { 071 super(field, rowDimension, columnDimension); 072 data = MathArrays.buildArray(field, rowDimension, columnDimension); 073 } 074 075 /** 076 * Create a new {@code FieldMatrix<T>} using the input array as the underlying 077 * data array. 078 * <p>The input array is copied, not referenced. This constructor has 079 * the same effect as calling {@link #Array2DRowFieldMatrix(FieldElement[][], boolean)} 080 * with the second argument set to {@code true}.</p> 081 * 082 * @param d Data for the new matrix. 083 * @throws DimensionMismatchException if {@code d} is not rectangular. 084 * @throws NullArgumentException if {@code d} is {@code null}. 085 * @throws NoDataException if there are not at least one row and one column. 086 * @see #Array2DRowFieldMatrix(FieldElement[][], boolean) 087 */ 088 public Array2DRowFieldMatrix(final T[][] d) 089 throws DimensionMismatchException, NullArgumentException, 090 NoDataException { 091 this(extractField(d), d); 092 } 093 094 /** 095 * Create a new {@code FieldMatrix<T>} using the input array as the underlying 096 * data array. 097 * <p>The input array is copied, not referenced. This constructor has 098 * the same effect as calling {@link #Array2DRowFieldMatrix(FieldElement[][], boolean)} 099 * with the second argument set to {@code true}.</p> 100 * 101 * @param field Field to which the elements belong. 102 * @param d Data for the new matrix. 103 * @throws DimensionMismatchException if {@code d} is not rectangular. 104 * @throws NullArgumentException if {@code d} is {@code null}. 105 * @throws NoDataException if there are not at least one row and one column. 106 * @see #Array2DRowFieldMatrix(FieldElement[][], boolean) 107 */ 108 public Array2DRowFieldMatrix(final Field<T> field, final T[][] d) 109 throws DimensionMismatchException, NullArgumentException, 110 NoDataException { 111 super(field); 112 copyIn(d); 113 } 114 115 /** 116 * Create a new {@code FieldMatrix<T>} using the input array as the underlying 117 * data array. 118 * <p>If an array is built specially in order to be embedded in a 119 * {@code FieldMatrix<T>} and not used directly, the {@code copyArray} may be 120 * set to {@code false}. This will prevent the copying and improve 121 * performance as no new array will be built and no data will be copied.</p> 122 * 123 * @param d Data for the new matrix. 124 * @param copyArray Whether to copy or reference the input array. 125 * @throws DimensionMismatchException if {@code d} is not rectangular. 126 * @throws NoDataException if there are not at least one row and one column. 127 * @throws NullArgumentException if {@code d} is {@code null}. 128 * @see #Array2DRowFieldMatrix(FieldElement[][]) 129 */ 130 public Array2DRowFieldMatrix(final T[][] d, final boolean copyArray) 131 throws DimensionMismatchException, NoDataException, 132 NullArgumentException { 133 this(extractField(d), d, copyArray); 134 } 135 136 /** 137 * Create a new {@code FieldMatrix<T>} using the input array as the underlying 138 * data array. 139 * <p>If an array is built specially in order to be embedded in a 140 * {@code FieldMatrix<T>} and not used directly, the {@code copyArray} may be 141 * set to {@code false}. This will prevent the copying and improve 142 * performance as no new array will be built and no data will be copied.</p> 143 * 144 * @param field Field to which the elements belong. 145 * @param d Data for the new matrix. 146 * @param copyArray Whether to copy or reference the input array. 147 * @throws DimensionMismatchException if {@code d} is not rectangular. 148 * @throws NoDataException if there are not at least one row and one column. 149 * @throws NullArgumentException if {@code d} is {@code null}. 150 * @see #Array2DRowFieldMatrix(FieldElement[][]) 151 */ 152 public Array2DRowFieldMatrix(final Field<T> field, final T[][] d, final boolean copyArray) 153 throws DimensionMismatchException, NoDataException, NullArgumentException { 154 super(field); 155 if (copyArray) { 156 copyIn(d); 157 } else { 158 NullArgumentException.check(d); 159 final int nRows = d.length; 160 if (nRows == 0) { 161 throw new NoDataException(LocalizedFormats.AT_LEAST_ONE_ROW); 162 } 163 final int nCols = d[0].length; 164 if (nCols == 0) { 165 throw new NoDataException(LocalizedFormats.AT_LEAST_ONE_COLUMN); 166 } 167 for (int r = 1; r < nRows; r++) { 168 if (d[r].length != nCols) { 169 throw new DimensionMismatchException(nCols, d[r].length); 170 } 171 } 172 data = d; 173 } 174 } 175 176 /** 177 * Create a new (column) {@code FieldMatrix<T>} using {@code v} as the 178 * data for the unique column of the created matrix. 179 * The input array is copied. 180 * 181 * @param v Column vector holding data for new matrix. 182 * @throws NoDataException if v is empty 183 */ 184 public Array2DRowFieldMatrix(final T[] v) throws NoDataException { 185 this(extractField(v), v); 186 } 187 188 /** 189 * Create a new (column) {@code FieldMatrix<T>} using {@code v} as the 190 * data for the unique column of the created matrix. 191 * The input array is copied. 192 * 193 * @param field Field to which the elements belong. 194 * @param v Column vector holding data for new matrix. 195 */ 196 public Array2DRowFieldMatrix(final Field<T> field, final T[] v) { 197 super(field); 198 final int nRows = v.length; 199 data = MathArrays.buildArray(getField(), nRows, 1); 200 for (int row = 0; row < nRows; row++) { 201 data[row][0] = v[row]; 202 } 203 } 204 205 /** {@inheritDoc} */ 206 @Override 207 public FieldMatrix<T> createMatrix(final int rowDimension, 208 final int columnDimension) 209 throws NotStrictlyPositiveException { 210 return new Array2DRowFieldMatrix<>(getField(), rowDimension, columnDimension); 211 } 212 213 /** {@inheritDoc} */ 214 @Override 215 public FieldMatrix<T> copy() { 216 return new Array2DRowFieldMatrix<>(getField(), copyOut(), false); 217 } 218 219 /** 220 * Add {@code m} to this matrix. 221 * 222 * @param m Matrix to be added. 223 * @return {@code this} + m. 224 * @throws MatrixDimensionMismatchException if {@code m} is not the same 225 * size as this matrix. 226 */ 227 public Array2DRowFieldMatrix<T> add(final Array2DRowFieldMatrix<T> m) 228 throws MatrixDimensionMismatchException { 229 // safety check 230 checkAdd(m); 231 232 final int rowCount = getRowDimension(); 233 final int columnCount = getColumnDimension(); 234 final T[][] outData = MathArrays.buildArray(getField(), rowCount, columnCount); 235 for (int row = 0; row < rowCount; row++) { 236 final T[] dataRow = data[row]; 237 final T[] mRow = m.data[row]; 238 final T[] outDataRow = outData[row]; 239 for (int col = 0; col < columnCount; col++) { 240 outDataRow[col] = dataRow[col].add(mRow[col]); 241 } 242 } 243 244 return new Array2DRowFieldMatrix<>(getField(), outData, false); 245 } 246 247 /** 248 * Subtract {@code m} from this matrix. 249 * 250 * @param m Matrix to be subtracted. 251 * @return {@code this} + m. 252 * @throws MatrixDimensionMismatchException if {@code m} is not the same 253 * size as this matrix. 254 */ 255 public Array2DRowFieldMatrix<T> subtract(final Array2DRowFieldMatrix<T> m) 256 throws MatrixDimensionMismatchException { 257 // safety check 258 checkAdd(m); 259 260 final int rowCount = getRowDimension(); 261 final int columnCount = getColumnDimension(); 262 final T[][] outData = MathArrays.buildArray(getField(), rowCount, columnCount); 263 for (int row = 0; row < rowCount; row++) { 264 final T[] dataRow = data[row]; 265 final T[] mRow = m.data[row]; 266 final T[] outDataRow = outData[row]; 267 for (int col = 0; col < columnCount; col++) { 268 outDataRow[col] = dataRow[col].subtract(mRow[col]); 269 } 270 } 271 272 return new Array2DRowFieldMatrix<>(getField(), outData, false); 273 } 274 275 /** 276 * Postmultiplying this matrix by {@code m}. 277 * 278 * @param m Matrix to postmultiply by. 279 * @return {@code this} * m. 280 * @throws DimensionMismatchException if the number of columns of this 281 * matrix is not equal to the number of rows of {@code m}. 282 */ 283 public Array2DRowFieldMatrix<T> multiply(final Array2DRowFieldMatrix<T> m) 284 throws DimensionMismatchException { 285 // safety check 286 checkMultiply(m); 287 288 final int nRows = this.getRowDimension(); 289 final int nCols = m.getColumnDimension(); 290 final int nSum = this.getColumnDimension(); 291 final T[][] outData = MathArrays.buildArray(getField(), nRows, nCols); 292 for (int row = 0; row < nRows; row++) { 293 final T[] dataRow = data[row]; 294 final T[] outDataRow = outData[row]; 295 for (int col = 0; col < nCols; col++) { 296 T sum = getField().getZero(); 297 for (int i = 0; i < nSum; i++) { 298 sum = sum.add(dataRow[i].multiply(m.data[i][col])); 299 } 300 outDataRow[col] = sum; 301 } 302 } 303 304 return new Array2DRowFieldMatrix<>(getField(), outData, false); 305 } 306 307 /** {@inheritDoc} */ 308 @Override 309 public T[][] getData() { 310 return copyOut(); 311 } 312 313 /** 314 * Get a reference to the underlying data array. 315 * This methods returns internal data, <strong>not</strong> fresh copy of it. 316 * 317 * @return the 2-dimensional array of entries. 318 */ 319 public T[][] getDataRef() { 320 return data; 321 } 322 323 /** {@inheritDoc} */ 324 @Override 325 public void setSubMatrix(final T[][] subMatrix, final int row, 326 final int column) 327 throws OutOfRangeException, NullArgumentException, NoDataException, 328 DimensionMismatchException { 329 if (data == null) { 330 if (row > 0) { 331 throw new MathIllegalStateException(LocalizedFormats.FIRST_ROWS_NOT_INITIALIZED_YET, row); 332 } 333 if (column > 0) { 334 throw new MathIllegalStateException(LocalizedFormats.FIRST_COLUMNS_NOT_INITIALIZED_YET, column); 335 } 336 final int nRows = subMatrix.length; 337 if (nRows == 0) { 338 throw new NoDataException(LocalizedFormats.AT_LEAST_ONE_ROW); 339 } 340 341 final int nCols = subMatrix[0].length; 342 if (nCols == 0) { 343 throw new NoDataException(LocalizedFormats.AT_LEAST_ONE_COLUMN); 344 } 345 data = MathArrays.buildArray(getField(), subMatrix.length, nCols); 346 for (int i = 0; i < data.length; ++i) { 347 if (subMatrix[i].length != nCols) { 348 throw new DimensionMismatchException(nCols, subMatrix[i].length); 349 } 350 System.arraycopy(subMatrix[i], 0, data[i + row], column, nCols); 351 } 352 } else { 353 super.setSubMatrix(subMatrix, row, column); 354 } 355 } 356 357 /** {@inheritDoc} */ 358 @Override 359 public T getEntry(final int row, final int column) 360 throws OutOfRangeException { 361 checkRowIndex(row); 362 checkColumnIndex(column); 363 364 return data[row][column]; 365 } 366 367 /** {@inheritDoc} */ 368 @Override 369 public void setEntry(final int row, final int column, final T value) 370 throws OutOfRangeException { 371 checkRowIndex(row); 372 checkColumnIndex(column); 373 374 data[row][column] = value; 375 } 376 377 /** {@inheritDoc} */ 378 @Override 379 public void addToEntry(final int row, final int column, final T increment) 380 throws OutOfRangeException { 381 checkRowIndex(row); 382 checkColumnIndex(column); 383 384 data[row][column] = data[row][column].add(increment); 385 } 386 387 /** {@inheritDoc} */ 388 @Override 389 public void multiplyEntry(final int row, final int column, final T factor) 390 throws OutOfRangeException { 391 checkRowIndex(row); 392 checkColumnIndex(column); 393 394 data[row][column] = data[row][column].multiply(factor); 395 } 396 397 /** {@inheritDoc} */ 398 @Override 399 public int getRowDimension() { 400 return (data == null) ? 0 : data.length; 401 } 402 403 /** {@inheritDoc} */ 404 @Override 405 public int getColumnDimension() { 406 return (data == null || data[0] == null) ? 0 : data[0].length; 407 } 408 409 /** {@inheritDoc} */ 410 @Override 411 public T[] operate(final T[] v) throws DimensionMismatchException { 412 final int nRows = this.getRowDimension(); 413 final int nCols = this.getColumnDimension(); 414 if (v.length != nCols) { 415 throw new DimensionMismatchException(v.length, nCols); 416 } 417 final T[] out = MathArrays.buildArray(getField(), nRows); 418 for (int row = 0; row < nRows; row++) { 419 final T[] dataRow = data[row]; 420 T sum = getField().getZero(); 421 for (int i = 0; i < nCols; i++) { 422 sum = sum.add(dataRow[i].multiply(v[i])); 423 } 424 out[row] = sum; 425 } 426 return out; 427 } 428 429 /** {@inheritDoc} */ 430 @Override 431 public T[] preMultiply(final T[] v) throws DimensionMismatchException { 432 final int nRows = getRowDimension(); 433 final int nCols = getColumnDimension(); 434 if (v.length != nRows) { 435 throw new DimensionMismatchException(v.length, nRows); 436 } 437 438 final T[] out = MathArrays.buildArray(getField(), nCols); 439 for (int col = 0; col < nCols; ++col) { 440 T sum = getField().getZero(); 441 for (int i = 0; i < nRows; ++i) { 442 sum = sum.add(data[i][col].multiply(v[i])); 443 } 444 out[col] = sum; 445 } 446 447 return out; 448 } 449 450 /** {@inheritDoc} */ 451 @Override 452 public T walkInRowOrder(final FieldMatrixChangingVisitor<T> visitor) { 453 final int rows = getRowDimension(); 454 final int columns = getColumnDimension(); 455 visitor.start(rows, columns, 0, rows - 1, 0, columns - 1); 456 for (int i = 0; i < rows; ++i) { 457 final T[] rowI = data[i]; 458 for (int j = 0; j < columns; ++j) { 459 rowI[j] = visitor.visit(i, j, rowI[j]); 460 } 461 } 462 return visitor.end(); 463 } 464 465 /** {@inheritDoc} */ 466 @Override 467 public T walkInRowOrder(final FieldMatrixPreservingVisitor<T> visitor) { 468 final int rows = getRowDimension(); 469 final int columns = getColumnDimension(); 470 visitor.start(rows, columns, 0, rows - 1, 0, columns - 1); 471 for (int i = 0; i < rows; ++i) { 472 final T[] rowI = data[i]; 473 for (int j = 0; j < columns; ++j) { 474 visitor.visit(i, j, rowI[j]); 475 } 476 } 477 return visitor.end(); 478 } 479 480 /** {@inheritDoc} */ 481 @Override 482 public T walkInRowOrder(final FieldMatrixChangingVisitor<T> visitor, 483 final int startRow, final int endRow, 484 final int startColumn, final int endColumn) 485 throws OutOfRangeException, NumberIsTooSmallException { 486 checkSubMatrixIndex(startRow, endRow, startColumn, endColumn); 487 visitor.start(getRowDimension(), getColumnDimension(), 488 startRow, endRow, startColumn, endColumn); 489 for (int i = startRow; i <= endRow; ++i) { 490 final T[] rowI = data[i]; 491 for (int j = startColumn; j <= endColumn; ++j) { 492 rowI[j] = visitor.visit(i, j, rowI[j]); 493 } 494 } 495 return visitor.end(); 496 } 497 498 /** {@inheritDoc} */ 499 @Override 500 public T walkInRowOrder(final FieldMatrixPreservingVisitor<T> visitor, 501 final int startRow, final int endRow, 502 final int startColumn, final int endColumn) 503 throws OutOfRangeException, NumberIsTooSmallException { 504 checkSubMatrixIndex(startRow, endRow, startColumn, endColumn); 505 visitor.start(getRowDimension(), getColumnDimension(), 506 startRow, endRow, startColumn, endColumn); 507 for (int i = startRow; i <= endRow; ++i) { 508 final T[] rowI = data[i]; 509 for (int j = startColumn; j <= endColumn; ++j) { 510 visitor.visit(i, j, rowI[j]); 511 } 512 } 513 return visitor.end(); 514 } 515 516 /** {@inheritDoc} */ 517 @Override 518 public T walkInColumnOrder(final FieldMatrixChangingVisitor<T> visitor) { 519 final int rows = getRowDimension(); 520 final int columns = getColumnDimension(); 521 visitor.start(rows, columns, 0, rows - 1, 0, columns - 1); 522 for (int j = 0; j < columns; ++j) { 523 for (int i = 0; i < rows; ++i) { 524 final T[] rowI = data[i]; 525 rowI[j] = visitor.visit(i, j, rowI[j]); 526 } 527 } 528 return visitor.end(); 529 } 530 531 /** {@inheritDoc} */ 532 @Override 533 public T walkInColumnOrder(final FieldMatrixPreservingVisitor<T> visitor) { 534 final int rows = getRowDimension(); 535 final int columns = getColumnDimension(); 536 visitor.start(rows, columns, 0, rows - 1, 0, columns - 1); 537 for (int j = 0; j < columns; ++j) { 538 for (int i = 0; i < rows; ++i) { 539 visitor.visit(i, j, data[i][j]); 540 } 541 } 542 return visitor.end(); 543 } 544 545 /** {@inheritDoc} */ 546 @Override 547 public T walkInColumnOrder(final FieldMatrixChangingVisitor<T> visitor, 548 final int startRow, final int endRow, 549 final int startColumn, final int endColumn) 550 throws OutOfRangeException, NumberIsTooSmallException { 551 checkSubMatrixIndex(startRow, endRow, startColumn, endColumn); 552 visitor.start(getRowDimension(), getColumnDimension(), 553 startRow, endRow, startColumn, endColumn); 554 for (int j = startColumn; j <= endColumn; ++j) { 555 for (int i = startRow; i <= endRow; ++i) { 556 final T[] rowI = data[i]; 557 rowI[j] = visitor.visit(i, j, rowI[j]); 558 } 559 } 560 return visitor.end(); 561 } 562 563 /** {@inheritDoc} */ 564 @Override 565 public T walkInColumnOrder(final FieldMatrixPreservingVisitor<T> visitor, 566 final int startRow, final int endRow, 567 final int startColumn, final int endColumn) 568 throws OutOfRangeException, NumberIsTooSmallException { 569 checkSubMatrixIndex(startRow, endRow, startColumn, endColumn); 570 visitor.start(getRowDimension(), getColumnDimension(), 571 startRow, endRow, startColumn, endColumn); 572 for (int j = startColumn; j <= endColumn; ++j) { 573 for (int i = startRow; i <= endRow; ++i) { 574 visitor.visit(i, j, data[i][j]); 575 } 576 } 577 return visitor.end(); 578 } 579 580 /** 581 * Get a fresh copy of the underlying data array. 582 * 583 * @return a copy of the underlying data array. 584 */ 585 private T[][] copyOut() { 586 final int nRows = this.getRowDimension(); 587 final T[][] out = MathArrays.buildArray(getField(), nRows, getColumnDimension()); 588 // can't copy 2-d array in one shot, otherwise get row references 589 for (int i = 0; i < nRows; i++) { 590 System.arraycopy(data[i], 0, out[i], 0, data[i].length); 591 } 592 return out; 593 } 594 595 /** 596 * Replace data with a fresh copy of the input array. 597 * 598 * @param in Data to copy. 599 * @throws NoDataException if the input array is empty. 600 * @throws DimensionMismatchException if the input array is not rectangular. 601 * @throws NullArgumentException if the input array is {@code null}. 602 */ 603 private void copyIn(final T[][] in) 604 throws NullArgumentException, NoDataException, 605 DimensionMismatchException { 606 setSubMatrix(in, 0, 0); 607 } 608}