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