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