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   */
37  public class Array2DRowRealMatrix extends AbstractRealMatrix implements Serializable {
38      /** Serializable version identifier. */
39      private static final long serialVersionUID = -1067294169172445528L;
40  
41      /** Entries of the matrix. */
42      private double data[][];
43  
44      /**
45       * Creates a matrix with no data
46       */
47      public Array2DRowRealMatrix() {}
48  
49      /**
50       * Create a new RealMatrix with the supplied row and column dimensions.
51       *
52       * @param rowDimension Number of rows in the new matrix.
53       * @param columnDimension Number of columns in the new matrix.
54       * @throws NotStrictlyPositiveException if the row or column dimension is
55       * not positive.
56       */
57      public Array2DRowRealMatrix(final int rowDimension,
58                                  final int columnDimension)
59          throws NotStrictlyPositiveException {
60          super(rowDimension, columnDimension);
61          data = new double[rowDimension][columnDimension];
62      }
63  
64      /**
65       * Create a new {@code RealMatrix} using the input array as the underlying
66       * data array.
67       * <p>The input array is copied, not referenced. This constructor has
68       * the same effect as calling {@link #Array2DRowRealMatrix(double[][], boolean)}
69       * with the second argument set to {@code true}.</p>
70       *
71       * @param d Data for the new matrix.
72       * @throws DimensionMismatchException if {@code d} is not rectangular.
73       * @throws NoDataException if {@code d} row or column dimension is zero.
74       * @throws NullArgumentException if {@code d} is {@code null}.
75       * @see #Array2DRowRealMatrix(double[][], boolean)
76       */
77      public Array2DRowRealMatrix(final double[][] d)
78          throws DimensionMismatchException, NoDataException, NullArgumentException {
79          copyIn(d);
80      }
81  
82      /**
83       * Create a new RealMatrix using the input array as the underlying
84       * data array.
85       * If an array is built specially in order to be embedded in a
86       * RealMatrix and not used directly, the {@code copyArray} may be
87       * set to {@code false}. This will prevent the copying and improve
88       * performance as no new array will be built and no data will be copied.
89       *
90       * @param d Data for new matrix.
91       * @param copyArray if {@code true}, the input array will be copied,
92       * otherwise it will be referenced.
93       * @throws DimensionMismatchException if {@code d} is not rectangular.
94       * @throws NoDataException if {@code d} row or column dimension is zero.
95       * @throws NullArgumentException if {@code d} is {@code null}.
96       * @see #Array2DRowRealMatrix(double[][])
97       */
98      public Array2DRowRealMatrix(final double[][] d, final boolean copyArray)
99          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 }