View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.commons.math3.linear;
19  
20  import java.io.Serializable;
21  
22  import org.apache.commons.math3.exception.DimensionMismatchException;
23  import org.apache.commons.math3.exception.MathIllegalStateException;
24  import org.apache.commons.math3.exception.NoDataException;
25  import org.apache.commons.math3.exception.NotStrictlyPositiveException;
26  import org.apache.commons.math3.exception.NullArgumentException;
27  import org.apache.commons.math3.exception.NumberIsTooSmallException;
28  import org.apache.commons.math3.exception.OutOfRangeException;
29  import org.apache.commons.math3.exception.util.LocalizedFormats;
30  import org.apache.commons.math3.util.MathUtils;
31  
32  /**
33   * Implementation of {@link RealMatrix} using a {@code double[][]} array to
34   * store entries.
35   *
36   * @version $Id: Array2DRowRealMatrix.java 1459082 2013-03-20 22:24:09Z tn $
37   */
38  public class Array2DRowRealMatrix extends AbstractRealMatrix implements Serializable {
39      /** Serializable version identifier. */
40      private static final long serialVersionUID = -1067294169172445528L;
41  
42      /** Entries of the matrix. */
43      private double data[][];
44  
45      /**
46       * Creates a matrix with no data
47       */
48      public Array2DRowRealMatrix() {}
49  
50      /**
51       * Create a new RealMatrix with the supplied row and column dimensions.
52       *
53       * @param rowDimension Number of rows in the new matrix.
54       * @param columnDimension Number of columns in the new matrix.
55       * @throws NotStrictlyPositiveException if the row or column dimension is
56       * not positive.
57       */
58      public Array2DRowRealMatrix(final int rowDimension,
59                                  final int columnDimension)
60          throws NotStrictlyPositiveException {
61          super(rowDimension, columnDimension);
62          data = new double[rowDimension][columnDimension];
63      }
64  
65      /**
66       * Create a new {@code RealMatrix} using the input array as the underlying
67       * data array.
68       * <p>The input array is copied, not referenced. This constructor has
69       * the same effect as calling {@link #Array2DRowRealMatrix(double[][], boolean)}
70       * with the second argument set to {@code true}.</p>
71       *
72       * @param d Data for the new matrix.
73       * @throws DimensionMismatchException if {@code d} is not rectangular.
74       * @throws NoDataException if {@code d} row or column dimension is zero.
75       * @throws NullArgumentException if {@code d} is {@code null}.
76       * @see #Array2DRowRealMatrix(double[][], boolean)
77       */
78      public Array2DRowRealMatrix(final double[][] d)
79          throws DimensionMismatchException, NoDataException, NullArgumentException {
80          copyIn(d);
81      }
82  
83      /**
84       * Create a new RealMatrix using the input array as the underlying
85       * data array.
86       * If an array is built specially in order to be embedded in a
87       * RealMatrix and not used directly, the {@code copyArray} may be
88       * set to {@code false}. This will prevent the copying and improve
89       * performance as no new array will be built and no data will be copied.
90       *
91       * @param d Data for new matrix.
92       * @param copyArray if {@code true}, the input array will be copied,
93       * otherwise it will be referenced.
94       * @throws DimensionMismatchException if {@code d} is not rectangular.
95       * @throws NoDataException if {@code d} row or column dimension is zero.
96       * @throws NullArgumentException if {@code d} is {@code null}.
97       * @see #Array2DRowRealMatrix(double[][])
98       */
99      public Array2DRowRealMatrix(final double[][] d, final boolean copyArray)
100         throws DimensionMismatchException, NoDataException,
101         NullArgumentException {
102         if (copyArray) {
103             copyIn(d);
104         } else {
105             if (d == null) {
106                 throw new NullArgumentException();
107             }
108             final int nRows = d.length;
109             if (nRows == 0) {
110                 throw new NoDataException(LocalizedFormats.AT_LEAST_ONE_ROW);
111             }
112             final int nCols = d[0].length;
113             if (nCols == 0) {
114                 throw new NoDataException(LocalizedFormats.AT_LEAST_ONE_COLUMN);
115             }
116             for (int r = 1; r < nRows; r++) {
117                 if (d[r].length != nCols) {
118                     throw new DimensionMismatchException(d[r].length, nCols);
119                 }
120             }
121             data = d;
122         }
123     }
124 
125     /**
126      * Create a new (column) RealMatrix using {@code v} as the
127      * data for the unique column of the created matrix.
128      * The input array is copied.
129      *
130      * @param v Column vector holding data for new matrix.
131      */
132     public Array2DRowRealMatrix(final double[] v) {
133         final int nRows = v.length;
134         data = new double[nRows][1];
135         for (int row = 0; row < nRows; row++) {
136             data[row][0] = v[row];
137         }
138     }
139 
140     /** {@inheritDoc} */
141     @Override
142     public RealMatrix createMatrix(final int rowDimension,
143                                    final int columnDimension)
144         throws NotStrictlyPositiveException {
145         return new Array2DRowRealMatrix(rowDimension, columnDimension);
146     }
147 
148     /** {@inheritDoc} */
149     @Override
150     public RealMatrix copy() {
151         return new Array2DRowRealMatrix(copyOut(), false);
152     }
153 
154     /**
155      * Compute the sum of {@code this} and {@code m}.
156      *
157      * @param m Matrix to be added.
158      * @return {@code this + m}.
159      * @throws MatrixDimensionMismatchException if {@code m} is not the same
160      * size as {@code this}.
161      */
162     public Array2DRowRealMatrix add(final Array2DRowRealMatrix m)
163         throws MatrixDimensionMismatchException {
164         // Safety check.
165         MatrixUtils.checkAdditionCompatible(this, m);
166 
167         final int rowCount    = getRowDimension();
168         final int columnCount = getColumnDimension();
169         final double[][] outData = new double[rowCount][columnCount];
170         for (int row = 0; row < rowCount; row++) {
171             final double[] dataRow    = data[row];
172             final double[] mRow       = m.data[row];
173             final double[] outDataRow = outData[row];
174             for (int col = 0; col < columnCount; col++) {
175                 outDataRow[col] = dataRow[col] + mRow[col];
176             }
177         }
178 
179         return new Array2DRowRealMatrix(outData, false);
180     }
181 
182     /**
183      * Returns {@code this} minus {@code m}.
184      *
185      * @param m Matrix to be subtracted.
186      * @return {@code this - m}
187      * @throws MatrixDimensionMismatchException if {@code m} is not the same
188      * size as {@code this}.
189      */
190     public Array2DRowRealMatrix subtract(final Array2DRowRealMatrix m)
191         throws MatrixDimensionMismatchException {
192         MatrixUtils.checkSubtractionCompatible(this, m);
193 
194         final int rowCount    = getRowDimension();
195         final int columnCount = getColumnDimension();
196         final double[][] outData = new double[rowCount][columnCount];
197         for (int row = 0; row < rowCount; row++) {
198             final double[] dataRow    = data[row];
199             final double[] mRow       = m.data[row];
200             final double[] outDataRow = outData[row];
201             for (int col = 0; col < columnCount; col++) {
202                 outDataRow[col] = dataRow[col] - mRow[col];
203             }
204         }
205 
206         return new Array2DRowRealMatrix(outData, false);
207     }
208 
209     /**
210      * Returns the result of postmultiplying {@code this} by {@code m}.
211      *
212      * @param m matrix to postmultiply by
213      * @return {@code this * m}
214      * @throws DimensionMismatchException if
215      * {@code columnDimension(this) != rowDimension(m)}
216      */
217     public Array2DRowRealMatrix multiply(final Array2DRowRealMatrix m)
218         throws DimensionMismatchException {
219         MatrixUtils.checkMultiplicationCompatible(this, m);
220 
221         final int nRows = this.getRowDimension();
222         final int nCols = m.getColumnDimension();
223         final int nSum = this.getColumnDimension();
224 
225         final double[][] outData = new double[nRows][nCols];
226         // Will hold a column of "m".
227         final double[] mCol = new double[nSum];
228         final double[][] mData = m.data;
229 
230         // Multiply.
231         for (int col = 0; col < nCols; col++) {
232             // Copy all elements of column "col" of "m" so that
233             // will be in contiguous memory.
234             for (int mRow = 0; mRow < nSum; mRow++) {
235                 mCol[mRow] = mData[mRow][col];
236             }
237 
238             for (int row = 0; row < nRows; row++) {
239                 final double[] dataRow = data[row];
240                 double sum = 0;
241                 for (int i = 0; i < nSum; i++) {
242                     sum += dataRow[i] * mCol[i];
243                 }
244                 outData[row][col] = sum;
245             }
246         }
247 
248         return new Array2DRowRealMatrix(outData, false);
249     }
250 
251     /** {@inheritDoc} */
252     @Override
253     public double[][] getData() {
254         return copyOut();
255     }
256 
257     /**
258      * Get a reference to the underlying data array.
259      *
260      * @return 2-dimensional array of entries.
261      */
262     public double[][] getDataRef() {
263         return data;
264     }
265 
266     /** {@inheritDoc} */
267     @Override
268     public void setSubMatrix(final double[][] subMatrix, final int row,
269                              final int column)
270         throws NoDataException, OutOfRangeException,
271         DimensionMismatchException, NullArgumentException {
272         if (data == null) {
273             if (row > 0) {
274                 throw new MathIllegalStateException(LocalizedFormats.FIRST_ROWS_NOT_INITIALIZED_YET, row);
275             }
276             if (column > 0) {
277                 throw new MathIllegalStateException(LocalizedFormats.FIRST_COLUMNS_NOT_INITIALIZED_YET, column);
278             }
279             MathUtils.checkNotNull(subMatrix);
280             final int nRows = subMatrix.length;
281             if (nRows == 0) {
282                 throw new NoDataException(LocalizedFormats.AT_LEAST_ONE_ROW);
283             }
284 
285             final int nCols = subMatrix[0].length;
286             if (nCols == 0) {
287                 throw new NoDataException(LocalizedFormats.AT_LEAST_ONE_COLUMN);
288             }
289             data = new double[subMatrix.length][nCols];
290             for (int i = 0; i < data.length; ++i) {
291                 if (subMatrix[i].length != nCols) {
292                     throw new DimensionMismatchException(subMatrix[i].length, nCols);
293                 }
294                 System.arraycopy(subMatrix[i], 0, data[i + row], column, nCols);
295             }
296         } else {
297             super.setSubMatrix(subMatrix, row, column);
298         }
299 
300     }
301 
302     /** {@inheritDoc} */
303     @Override
304     public double getEntry(final int row, final int column)
305         throws OutOfRangeException {
306         MatrixUtils.checkMatrixIndex(this, row, column);
307         return data[row][column];
308     }
309 
310     /** {@inheritDoc} */
311     @Override
312     public void setEntry(final int row, final int column, final double value)
313         throws OutOfRangeException {
314         MatrixUtils.checkMatrixIndex(this, row, column);
315         data[row][column] = value;
316     }
317 
318     /** {@inheritDoc} */
319     @Override
320     public void addToEntry(final int row, final int column,
321                            final double increment)
322         throws OutOfRangeException {
323         MatrixUtils.checkMatrixIndex(this, row, column);
324         data[row][column] += increment;
325     }
326 
327     /** {@inheritDoc} */
328     @Override
329     public void multiplyEntry(final int row, final int column,
330                               final double factor)
331         throws OutOfRangeException {
332         MatrixUtils.checkMatrixIndex(this, row, column);
333         data[row][column] *= factor;
334     }
335 
336     /** {@inheritDoc} */
337     @Override
338     public int getRowDimension() {
339         return (data == null) ? 0 : data.length;
340     }
341 
342     /** {@inheritDoc} */
343     @Override
344     public int getColumnDimension() {
345         return ((data == null) || (data[0] == null)) ? 0 : data[0].length;
346     }
347 
348     /** {@inheritDoc} */
349     @Override
350     public double[] operate(final double[] v)
351         throws DimensionMismatchException {
352         final int nRows = this.getRowDimension();
353         final int nCols = this.getColumnDimension();
354         if (v.length != nCols) {
355             throw new DimensionMismatchException(v.length, nCols);
356         }
357         final double[] out = new double[nRows];
358         for (int row = 0; row < nRows; row++) {
359             final double[] dataRow = data[row];
360             double sum = 0;
361             for (int i = 0; i < nCols; i++) {
362                 sum += dataRow[i] * v[i];
363             }
364             out[row] = sum;
365         }
366         return out;
367     }
368 
369     /** {@inheritDoc} */
370     @Override
371     public double[] preMultiply(final double[] v)
372         throws DimensionMismatchException {
373         final int nRows = getRowDimension();
374         final int nCols = getColumnDimension();
375         if (v.length != nRows) {
376             throw new DimensionMismatchException(v.length, nRows);
377         }
378 
379         final double[] out = new double[nCols];
380         for (int col = 0; col < nCols; ++col) {
381             double sum = 0;
382             for (int i = 0; i < nRows; ++i) {
383                 sum += data[i][col] * v[i];
384             }
385             out[col] = sum;
386         }
387 
388         return out;
389 
390     }
391 
392     /** {@inheritDoc} */
393     @Override
394     public double walkInRowOrder(final RealMatrixChangingVisitor visitor) {
395         final int rows    = getRowDimension();
396         final int columns = getColumnDimension();
397         visitor.start(rows, columns, 0, rows - 1, 0, columns - 1);
398         for (int i = 0; i < rows; ++i) {
399             final double[] rowI = data[i];
400             for (int j = 0; j < columns; ++j) {
401                 rowI[j] = visitor.visit(i, j, rowI[j]);
402             }
403         }
404         return visitor.end();
405     }
406 
407     /** {@inheritDoc} */
408     @Override
409     public double walkInRowOrder(final RealMatrixPreservingVisitor visitor) {
410         final int rows    = getRowDimension();
411         final int columns = getColumnDimension();
412         visitor.start(rows, columns, 0, rows - 1, 0, columns - 1);
413         for (int i = 0; i < rows; ++i) {
414             final double[] rowI = data[i];
415             for (int j = 0; j < columns; ++j) {
416                 visitor.visit(i, j, rowI[j]);
417             }
418         }
419         return visitor.end();
420     }
421 
422     /** {@inheritDoc} */
423     @Override
424     public double walkInRowOrder(final RealMatrixChangingVisitor visitor,
425                                  final int startRow, final int endRow,
426                                  final int startColumn, final int endColumn)
427         throws OutOfRangeException, NumberIsTooSmallException {
428         MatrixUtils.checkSubMatrixIndex(this, startRow, endRow, startColumn, endColumn);
429         visitor.start(getRowDimension(), getColumnDimension(),
430                       startRow, endRow, startColumn, endColumn);
431         for (int i = startRow; i <= endRow; ++i) {
432             final double[] rowI = data[i];
433             for (int j = startColumn; j <= endColumn; ++j) {
434                 rowI[j] = visitor.visit(i, j, rowI[j]);
435             }
436         }
437         return visitor.end();
438     }
439 
440     /** {@inheritDoc} */
441     @Override
442     public double walkInRowOrder(final RealMatrixPreservingVisitor visitor,
443                                  final int startRow, final int endRow,
444                                  final int startColumn, final int endColumn)
445         throws OutOfRangeException, NumberIsTooSmallException {
446         MatrixUtils.checkSubMatrixIndex(this, startRow, endRow, startColumn, endColumn);
447         visitor.start(getRowDimension(), getColumnDimension(),
448                       startRow, endRow, startColumn, endColumn);
449         for (int i = startRow; i <= endRow; ++i) {
450             final double[] rowI = data[i];
451             for (int j = startColumn; j <= endColumn; ++j) {
452                 visitor.visit(i, j, rowI[j]);
453             }
454         }
455         return visitor.end();
456     }
457 
458     /** {@inheritDoc} */
459     @Override
460     public double walkInColumnOrder(final RealMatrixChangingVisitor visitor) {
461         final int rows    = getRowDimension();
462         final int columns = getColumnDimension();
463         visitor.start(rows, columns, 0, rows - 1, 0, columns - 1);
464         for (int j = 0; j < columns; ++j) {
465             for (int i = 0; i < rows; ++i) {
466                 final double[] rowI = data[i];
467                 rowI[j] = visitor.visit(i, j, rowI[j]);
468             }
469         }
470         return visitor.end();
471     }
472 
473     /** {@inheritDoc} */
474     @Override
475     public double walkInColumnOrder(final RealMatrixPreservingVisitor visitor) {
476         final int rows    = getRowDimension();
477         final int columns = getColumnDimension();
478         visitor.start(rows, columns, 0, rows - 1, 0, columns - 1);
479         for (int j = 0; j < columns; ++j) {
480             for (int i = 0; i < rows; ++i) {
481                 visitor.visit(i, j, data[i][j]);
482             }
483         }
484         return visitor.end();
485     }
486 
487     /** {@inheritDoc} */
488     @Override
489     public double walkInColumnOrder(final RealMatrixChangingVisitor visitor,
490                                     final int startRow, final int endRow,
491                                     final int startColumn, final int endColumn)
492         throws OutOfRangeException, NumberIsTooSmallException {
493         MatrixUtils.checkSubMatrixIndex(this, startRow, endRow, startColumn, endColumn);
494         visitor.start(getRowDimension(), getColumnDimension(),
495                       startRow, endRow, startColumn, endColumn);
496         for (int j = startColumn; j <= endColumn; ++j) {
497             for (int i = startRow; i <= endRow; ++i) {
498                 final double[] rowI = data[i];
499                 rowI[j] = visitor.visit(i, j, rowI[j]);
500             }
501         }
502         return visitor.end();
503     }
504 
505     /** {@inheritDoc} */
506     @Override
507     public double walkInColumnOrder(final RealMatrixPreservingVisitor visitor,
508                                     final int startRow, final int endRow,
509                                     final int startColumn, final int endColumn)
510         throws OutOfRangeException, NumberIsTooSmallException {
511         MatrixUtils.checkSubMatrixIndex(this, startRow, endRow, startColumn, endColumn);
512         visitor.start(getRowDimension(), getColumnDimension(),
513                       startRow, endRow, startColumn, endColumn);
514         for (int j = startColumn; j <= endColumn; ++j) {
515             for (int i = startRow; i <= endRow; ++i) {
516                 visitor.visit(i, j, data[i][j]);
517             }
518         }
519         return visitor.end();
520     }
521 
522     /**
523      * Get a fresh copy of the underlying data array.
524      *
525      * @return a copy of the underlying data array.
526      */
527     private double[][] copyOut() {
528         final int nRows = this.getRowDimension();
529         final double[][] out = new double[nRows][this.getColumnDimension()];
530         // can't copy 2-d array in one shot, otherwise get row references
531         for (int i = 0; i < nRows; i++) {
532             System.arraycopy(data[i], 0, out[i], 0, data[i].length);
533         }
534         return out;
535     }
536 
537     /**
538      * Replace data with a fresh copy of the input array.
539      *
540      * @param in Data to copy.
541      * @throws NoDataException if the input array is empty.
542      * @throws DimensionMismatchException if the input array is not rectangular.
543      * @throws NullArgumentException if the input array is {@code null}.
544      */
545     private void copyIn(final double[][] in)
546         throws DimensionMismatchException, NoDataException, NullArgumentException {
547         setSubMatrix(in, 0, 0);
548     }
549 }