    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    }