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