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