View Javadoc
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.math4.legacy.linear;
19  
20  import java.io.Serializable;
21  
22  import org.apache.commons.math4.legacy.core.Field;
23  import org.apache.commons.math4.legacy.core.FieldElement;
24  import org.apache.commons.math4.legacy.exception.DimensionMismatchException;
25  import org.apache.commons.math4.legacy.exception.MathIllegalStateException;
26  import org.apache.commons.math4.legacy.exception.NoDataException;
27  import org.apache.commons.math4.legacy.exception.NotStrictlyPositiveException;
28  import org.apache.commons.math4.legacy.exception.NullArgumentException;
29  import org.apache.commons.math4.legacy.exception.NumberIsTooSmallException;
30  import org.apache.commons.math4.legacy.exception.OutOfRangeException;
31  import org.apache.commons.math4.legacy.exception.util.LocalizedFormats;
32  import org.apache.commons.math4.legacy.core.MathArrays;
33  
34  /**
35   * Implementation of {@code FieldMatrix<T>} using a {@link FieldElement}[][] array to store entries.
36   * <p>
37   * As specified in the {@link FieldMatrix} interface, matrix element indexing
38   * is 0-based -- e.g., <code>getEntry(0, 0)</code>
39   * returns the element in the first row, first column of the matrix.
40   * </p>
41   *
42   * @param <T> the type of the field elements
43   */
44  public class Array2DRowFieldMatrix<T extends FieldElement<T>>
45      extends AbstractFieldMatrix<T>
46      implements Serializable {
47      /** Serializable version identifier. */
48      private static final long serialVersionUID = 7260756672015356458L;
49      /** Entries of the matrix. */
50      private T[][] data;
51  
52      /**
53       * Creates a matrix with no data.
54       * @param field field to which the elements belong
55       */
56      public Array2DRowFieldMatrix(final Field<T> field) {
57          super(field);
58      }
59  
60      /**
61       * Create a new {@code FieldMatrix<T>} with the supplied row and column dimensions.
62       *
63       * @param field Field to which the elements belong.
64       * @param rowDimension Number of rows in the new matrix.
65       * @param columnDimension Number of columns in the new matrix.
66       * @throws NotStrictlyPositiveException if row or column dimension is not positive.
67       */
68      public Array2DRowFieldMatrix(final Field<T> field, final int rowDimension,
69                                   final int columnDimension)
70          throws NotStrictlyPositiveException {
71          super(field, rowDimension, columnDimension);
72          data = MathArrays.buildArray(field, rowDimension, columnDimension);
73      }
74  
75      /**
76       * Create a new {@code FieldMatrix<T>} using the input array as the underlying
77       * data array.
78       * <p>The input array is copied, not referenced. This constructor has
79       * the same effect as calling {@link #Array2DRowFieldMatrix(FieldElement[][], boolean)}
80       * with the second argument set to {@code true}.</p>
81       *
82       * @param d Data for the new matrix.
83       * @throws DimensionMismatchException if {@code d} is not rectangular.
84       * @throws NullArgumentException if {@code d} is {@code null}.
85       * @throws NoDataException if there are not at least one row and one column.
86       * @see #Array2DRowFieldMatrix(FieldElement[][], boolean)
87       */
88      public Array2DRowFieldMatrix(final T[][] d)
89          throws DimensionMismatchException, NullArgumentException,
90          NoDataException {
91          this(extractField(d), d);
92      }
93  
94      /**
95       * Create a new {@code FieldMatrix<T>} using the input array as the underlying
96       * data array.
97       * <p>The input array is copied, not referenced. This constructor has
98       * the same effect as calling {@link #Array2DRowFieldMatrix(FieldElement[][], boolean)}
99       * 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 }