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