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.math.linear;
19  
20  import java.io.Serializable;
21  
22  import org.apache.commons.math.Field;
23  import org.apache.commons.math.FieldElement;
24  import org.apache.commons.math.MathRuntimeException;
25  
26  /**
27   * Implementation of FieldMatrix<T> using a {@link FieldElement}[][] array to store entries.
28   * <p>
29   * As specified in the {@link FieldMatrix} interface, matrix element indexing
30   * is 0-based -- e.g., <code>getEntry(0, 0)</code>
31   * returns the element in the first row, first column of the matrix.</li></ul>
32   * </p>
33   *
34   * @param <T> the type of the field elements
35   * @version $Revision: 783702 $ $Date: 2009-06-11 04:54:02 -0400 (Thu, 11 Jun 2009) $
36   */
37  public class Array2DRowFieldMatrix<T extends FieldElement<T>> extends AbstractFieldMatrix<T> implements Serializable {
38      
39      /** Serializable version identifier */
40      private static final long serialVersionUID = 7260756672015356458L;
41  
42      /** Entries of the matrix */
43      protected T[][] data;
44  
45      /**
46       * Creates a matrix with no data
47       * @param field field to which the elements belong
48       */
49      public Array2DRowFieldMatrix(final Field<T> field) {
50          super(field);
51      }
52  
53      /**
54       * Create a new FieldMatrix<T> with the supplied row and column dimensions.
55       *
56       * @param field field to which the elements belong
57       * @param rowDimension  the number of rows in the new matrix
58       * @param columnDimension  the number of columns in the new matrix
59       * @throws IllegalArgumentException if row or column dimension is not
60       *  positive
61       */
62      public Array2DRowFieldMatrix(final Field<T> field,
63                             final int rowDimension, final int columnDimension)
64          throws IllegalArgumentException {
65          super(field, rowDimension, columnDimension);
66          data = buildArray(field, rowDimension, columnDimension);
67      }
68  
69      /**
70       * Create a new FieldMatrix<T> using the input array as the underlying
71       * data array.
72       * <p>The input array is copied, not referenced. This constructor has
73       * the same effect as calling {@link #Array2DRowFieldMatrix(FieldElement[][], boolean)}
74       * with the second argument set to <code>true</code>.</p>
75       *
76       * @param d data for new matrix
77       * @throws IllegalArgumentException if <code>d</code> is not rectangular
78       *  (not all rows have the same length) or empty
79       * @throws NullPointerException if <code>d</code> is null
80       * @see #Array2DRowFieldMatrix(FieldElement[][], boolean)
81       */
82      public Array2DRowFieldMatrix(final T[][] d)
83          throws IllegalArgumentException, NullPointerException {
84          super(extractField(d));
85          copyIn(d);
86      }
87  
88      /**
89       * Create a new FieldMatrix<T> using the input array as the underlying
90       * data array.
91       * <p>If an array is built specially in order to be embedded in a
92       * FieldMatrix<T> and not used directly, the <code>copyArray</code> may be
93       * set to <code>false</code. This will prevent the copying and improve
94       * performance as no new array will be built and no data will be copied.</p>
95       * @param d data for new matrix
96       * @param copyArray if true, the input array will be copied, otherwise
97       * it will be referenced
98       * @throws IllegalArgumentException if <code>d</code> is not rectangular
99       *  (not all rows have the same length) or empty
100      * @throws NullPointerException if <code>d</code> is null
101      * @see #Array2DRowFieldMatrix(FieldElement[][])
102      */
103     public Array2DRowFieldMatrix(final T[][] d, final boolean copyArray)
104         throws IllegalArgumentException, NullPointerException {
105         super(extractField(d));
106         if (copyArray) {
107             copyIn(d);
108         } else {
109             if (d == null) {
110                 throw new NullPointerException();
111             }   
112             final int nRows = d.length;
113             if (nRows == 0) {
114                 throw MathRuntimeException.createIllegalArgumentException("matrix must have at least one row"); 
115             }
116             final int nCols = d[0].length;
117             if (nCols == 0) {
118                 throw MathRuntimeException.createIllegalArgumentException("matrix must have at least one column"); 
119             }
120             for (int r = 1; r < nRows; r++) {
121                 if (d[r].length != nCols) {
122                     throw MathRuntimeException.createIllegalArgumentException(
123                             "some rows have length {0} while others have length {1}",
124                             nCols, d[r].length);
125                 }
126             }       
127             data = d;
128         }
129     }
130 
131     /**
132      * Create a new (column) FieldMatrix<T> using <code>v</code> as the
133      * data for the unique column of the <code>v.length x 1</code> matrix
134      * created.
135      * <p>The input array is copied, not referenced.</p>
136      *
137      * @param v column vector holding data for new matrix
138      */
139     public Array2DRowFieldMatrix(final T[] v) {
140         super(extractField(v));
141         final int nRows = v.length;
142         data = buildArray(getField(), nRows, 1);
143         for (int row = 0; row < nRows; row++) {
144             data[row][0] = v[row];
145         }
146     }
147 
148     /** {@inheritDoc} */
149     @Override
150     public FieldMatrix<T> createMatrix(final int rowDimension, final int columnDimension)
151         throws IllegalArgumentException {
152         return new Array2DRowFieldMatrix<T>(getField(), rowDimension, columnDimension);
153     }
154 
155     /** {@inheritDoc} */
156     @Override
157     public FieldMatrix<T> copy() {
158         return new Array2DRowFieldMatrix<T>(copyOut(), false);
159     }
160 
161     /** {@inheritDoc} */
162     @Override
163     public FieldMatrix<T> add(final FieldMatrix<T> m)
164         throws IllegalArgumentException {
165         try {
166             return add((Array2DRowFieldMatrix<T>) m);
167         } catch (ClassCastException cce) {
168             return super.add(m);
169         }
170     }
171 
172     /**
173      * Compute the sum of this and <code>m</code>.
174      *
175      * @param m    matrix to be added
176      * @return     this + m
177      * @throws  IllegalArgumentException if m is not the same size as this
178      */
179     public Array2DRowFieldMatrix<T> add(final Array2DRowFieldMatrix<T> m)
180         throws IllegalArgumentException {
181 
182         // safety check
183         checkAdditionCompatible(m);
184 
185         final int rowCount    = getRowDimension();
186         final int columnCount = getColumnDimension();
187         final T[][] outData = buildArray(getField(), rowCount, columnCount);
188         for (int row = 0; row < rowCount; row++) {
189             final T[] dataRow    = data[row];
190             final T[] mRow       = m.data[row];
191             final T[] outDataRow = outData[row];
192             for (int col = 0; col < columnCount; col++) {
193                 outDataRow[col] = dataRow[col].add(mRow[col]);
194             }
195         }
196 
197         return new Array2DRowFieldMatrix<T>(outData, false);
198 
199     }
200 
201     /** {@inheritDoc} */
202     @Override
203     public FieldMatrix<T> subtract(final FieldMatrix<T> m)
204         throws IllegalArgumentException {
205         try {
206             return subtract((Array2DRowFieldMatrix<T>) m);
207         } catch (ClassCastException cce) {
208             return super.subtract(m);
209         }
210     }
211 
212     /**
213      * Compute  this minus <code>m</code>.
214      *
215      * @param m    matrix to be subtracted
216      * @return     this + m
217      * @throws  IllegalArgumentException if m is not the same size as this
218      */
219     public Array2DRowFieldMatrix<T> subtract(final Array2DRowFieldMatrix<T> m)
220         throws IllegalArgumentException {
221 
222         // safety check
223         checkSubtractionCompatible(m);
224 
225         final int rowCount    = getRowDimension();
226         final int columnCount = getColumnDimension();
227         final T[][] outData = buildArray(getField(), rowCount, columnCount);
228         for (int row = 0; row < rowCount; row++) {
229             final T[] dataRow    = data[row];
230             final T[] mRow       = m.data[row];
231             final T[] outDataRow = outData[row];
232             for (int col = 0; col < columnCount; col++) {
233                 outDataRow[col] = dataRow[col].subtract(mRow[col]);
234             }
235         }
236 
237         return new Array2DRowFieldMatrix<T>(outData, false);
238 
239     }
240 
241     /** {@inheritDoc} */
242     @Override
243     public FieldMatrix<T> multiply(final FieldMatrix<T> m)
244         throws IllegalArgumentException {
245         try {
246             return multiply((Array2DRowFieldMatrix<T>) m);
247         } catch (ClassCastException cce) {
248             return super.multiply(m);
249         }
250     }
251 
252     /**
253      * Returns the result of postmultiplying this by <code>m</code>.
254      * @param m    matrix to postmultiply by
255      * @return     this*m
256      * @throws     IllegalArgumentException
257      *             if columnDimension(this) != rowDimension(m)
258      */
259     public Array2DRowFieldMatrix<T> multiply(final Array2DRowFieldMatrix<T> m)
260         throws IllegalArgumentException {
261 
262         // safety check
263         checkMultiplicationCompatible(m);
264 
265         final int nRows = this.getRowDimension();
266         final int nCols = m.getColumnDimension();
267         final int nSum = this.getColumnDimension();
268         final T[][] outData = buildArray(getField(), nRows, nCols);
269         for (int row = 0; row < nRows; row++) {
270             final T[] dataRow    = data[row];
271             final T[] outDataRow = outData[row];
272             for (int col = 0; col < nCols; col++) {
273                 T sum = getField().getZero();
274                 for (int i = 0; i < nSum; i++) {
275                     sum = sum.add(dataRow[i].multiply(m.data[i][col]));
276                 }
277                 outDataRow[col] = sum;
278             }
279         }
280 
281         return new Array2DRowFieldMatrix<T>(outData, false);
282 
283     }
284 
285     /** {@inheritDoc} */
286     @Override
287     public T[][] getData() {
288         return copyOut();
289     }
290 
291     /**
292      * Returns a reference to the underlying data array.
293      * <p>
294      * Does <strong>not</strong> make a fresh copy of the underlying data.</p>
295      *
296      * @return 2-dimensional array of entries
297      */
298     public T[][] getDataRef() {
299         return data;
300     }
301 
302     /** {@inheritDoc} */
303     @Override
304     public void setSubMatrix(final T[][] subMatrix, final int row, final int column) 
305     throws MatrixIndexException {
306         if (data == null) {
307             if (row > 0) {
308                 throw MathRuntimeException.createIllegalStateException(
309                         "first {0} rows are not initialized yet",
310                         row);
311             }
312             if (column > 0) {
313                 throw MathRuntimeException.createIllegalStateException(
314                         "first {0} columns are not initialized yet",
315                         column);
316             }
317             final int nRows = subMatrix.length;
318             if (nRows == 0) {
319                 throw MathRuntimeException.createIllegalArgumentException("matrix must have at least one row"); 
320             }
321 
322             final int nCols = subMatrix[0].length;
323             if (nCols == 0) {
324                 throw MathRuntimeException.createIllegalArgumentException("matrix must have at least one column"); 
325             }
326             data = buildArray(getField(), subMatrix.length, nCols);
327             for (int i = 0; i < data.length; ++i) {
328                 if (subMatrix[i].length != nCols) {
329                     throw MathRuntimeException.createIllegalArgumentException(
330                             "some rows have length {0} while others have length {1}",
331                             nCols, subMatrix[i].length); 
332                 }
333                 System.arraycopy(subMatrix[i], 0, data[i + row], column, nCols);
334             }
335         } else {
336             super.setSubMatrix(subMatrix, row, column);
337         }
338 
339     }
340 
341     /** {@inheritDoc} */
342     @Override
343     public T getEntry(final int row, final int column)
344         throws MatrixIndexException {
345         try {
346             return data[row][column];
347         } catch (ArrayIndexOutOfBoundsException e) {
348             throw new MatrixIndexException(
349                     "no entry at indices ({0}, {1}) in a {2}x{3} matrix",
350                     row, column, getRowDimension(), getColumnDimension());
351         }
352     }
353 
354     /** {@inheritDoc} */
355     @Override
356     public void setEntry(final int row, final int column, final T value)
357         throws MatrixIndexException {
358         try {
359             data[row][column] = value;
360         } catch (ArrayIndexOutOfBoundsException e) {
361             throw new MatrixIndexException(
362                     "no entry at indices ({0}, {1}) in a {2}x{3} matrix",
363                     row, column, getRowDimension(), getColumnDimension());
364         }
365     }
366 
367     /** {@inheritDoc} */
368     @Override
369     public void addToEntry(final int row, final int column, final T increment)
370         throws MatrixIndexException {
371         try {
372             data[row][column] = data[row][column].add(increment);
373         } catch (ArrayIndexOutOfBoundsException e) {
374             throw new MatrixIndexException(
375                     "no entry at indices ({0}, {1}) in a {2}x{3} matrix",
376                     row, column, getRowDimension(), getColumnDimension());
377         }      
378     }
379 
380     /** {@inheritDoc} */
381     @Override
382     public void multiplyEntry(final int row, final int column, final T factor)
383         throws MatrixIndexException {
384         try {
385             data[row][column] = data[row][column].multiply(factor);
386         } catch (ArrayIndexOutOfBoundsException e) {
387             throw new MatrixIndexException(
388                     "no entry at indices ({0}, {1}) in a {2}x{3} matrix",
389                     row, column, getRowDimension(), getColumnDimension());
390         }      
391     }
392 
393     /** {@inheritDoc} */
394     @Override
395     public int getRowDimension() {
396         return (data == null) ? 0 : data.length;
397     }
398 
399     /** {@inheritDoc} */
400     @Override
401     public int getColumnDimension() {
402         return ((data == null) || (data[0] == null)) ? 0 : data[0].length;
403     }
404 
405     /** {@inheritDoc} */
406     @Override
407     public T[] operate(final T[] v)
408         throws IllegalArgumentException {
409         final int nRows = this.getRowDimension();
410         final int nCols = this.getColumnDimension();
411         if (v.length != nCols) {
412             throw MathRuntimeException.createIllegalArgumentException(
413                     "vector length mismatch: got {0} but expected {1}",
414                     v.length, nCols);
415         }
416         final T[] out = buildArray(getField(), nRows);
417         for (int row = 0; row < nRows; row++) {
418             final T[] dataRow = data[row];
419             T sum = getField().getZero();
420             for (int i = 0; i < nCols; i++) {
421                 sum = sum.add(dataRow[i].multiply(v[i]));
422             }
423             out[row] = sum;
424         }
425         return out;
426     }
427 
428     /** {@inheritDoc} */
429     @Override
430     public T[] preMultiply(final T[] v)
431         throws IllegalArgumentException {
432 
433         final int nRows = getRowDimension();
434         final int nCols = getColumnDimension();
435         if (v.length != nRows) {
436             throw MathRuntimeException.createIllegalArgumentException(
437                     "vector length mismatch: got {0} but expected {1}",
438                     v.length, nRows);
439         }
440 
441         final T[] out = buildArray(getField(), nCols);
442         for (int col = 0; col < nCols; ++col) {
443             T sum = getField().getZero();
444             for (int i = 0; i < nRows; ++i) {
445                 sum = sum.add(data[i][col].multiply(v[i]));
446             }
447             out[col] = sum;
448         }
449 
450         return out;
451 
452     }
453 
454     /** {@inheritDoc} */
455     @Override
456     public T walkInRowOrder(final FieldMatrixChangingVisitor<T> visitor)
457         throws MatrixVisitorException {
458         final int rows    = getRowDimension();
459         final int columns = getColumnDimension();
460         visitor.start(rows, columns, 0, rows - 1, 0, columns - 1);
461         for (int i = 0; i < rows; ++i) {
462             final T[] rowI = data[i];
463             for (int j = 0; j < columns; ++j) {
464                 rowI[j] = visitor.visit(i, j, rowI[j]);
465             }
466         }
467         return visitor.end();
468     }
469 
470     /** {@inheritDoc} */
471     @Override
472     public T walkInRowOrder(final FieldMatrixPreservingVisitor<T> visitor)
473         throws MatrixVisitorException {
474         final int rows    = getRowDimension();
475         final int columns = getColumnDimension();
476         visitor.start(rows, columns, 0, rows - 1, 0, columns - 1);
477         for (int i = 0; i < rows; ++i) {
478             final T[] rowI = data[i];
479             for (int j = 0; j < columns; ++j) {
480                 visitor.visit(i, j, rowI[j]);
481             }
482         }
483         return visitor.end();
484     }
485 
486     /** {@inheritDoc} */
487     @Override
488     public T walkInRowOrder(final FieldMatrixChangingVisitor<T> visitor,
489                             final int startRow, final int endRow,
490                             final int startColumn, final int endColumn)
491         throws MatrixIndexException, MatrixVisitorException {
492         checkSubMatrixIndex(startRow, endRow, startColumn, endColumn);
493         visitor.start(getRowDimension(), getColumnDimension(),
494                       startRow, endRow, startColumn, endColumn);
495         for (int i = startRow; i <= endRow; ++i) {
496             final T[] rowI = data[i];
497             for (int j = startColumn; j <= endColumn; ++j) {
498                 rowI[j] = visitor.visit(i, j, rowI[j]);
499             }
500         }
501         return visitor.end();
502     }
503 
504     /** {@inheritDoc} */
505     @Override
506     public T walkInRowOrder(final FieldMatrixPreservingVisitor<T> visitor,
507                             final int startRow, final int endRow,
508                             final int startColumn, final int endColumn)
509         throws MatrixIndexException, MatrixVisitorException {
510         checkSubMatrixIndex(startRow, endRow, startColumn, endColumn);
511         visitor.start(getRowDimension(), getColumnDimension(),
512                       startRow, endRow, startColumn, endColumn);
513         for (int i = startRow; i <= endRow; ++i) {
514             final T[] rowI = data[i];
515             for (int j = startColumn; j <= endColumn; ++j) {
516                 visitor.visit(i, j, rowI[j]);
517             }
518         }
519         return visitor.end();
520     }
521 
522     /** {@inheritDoc} */
523     @Override
524     public T walkInColumnOrder(final FieldMatrixChangingVisitor<T> visitor)
525         throws MatrixVisitorException {
526         final int rows    = getRowDimension();
527         final int columns = getColumnDimension();
528         visitor.start(rows, columns, 0, rows - 1, 0, columns - 1);
529         for (int j = 0; j < columns; ++j) {
530             for (int i = 0; i < rows; ++i) {
531                 final T[] rowI = data[i];
532                 rowI[j] = visitor.visit(i, j, rowI[j]);
533             }
534         }
535         return visitor.end();
536     }
537 
538     /** {@inheritDoc} */
539     @Override
540     public T walkInColumnOrder(final FieldMatrixPreservingVisitor<T> visitor)
541         throws MatrixVisitorException {
542         final int rows    = getRowDimension();
543         final int columns = getColumnDimension();
544         visitor.start(rows, columns, 0, rows - 1, 0, columns - 1);
545         for (int j = 0; j < columns; ++j) {
546             for (int i = 0; i < rows; ++i) {
547                 visitor.visit(i, j, data[i][j]);
548             }
549         }
550         return visitor.end();
551     }
552 
553     /** {@inheritDoc} */
554     @Override
555     public T walkInColumnOrder(final FieldMatrixChangingVisitor<T> visitor,
556                                final int startRow, final int endRow,
557                                final int startColumn, final int endColumn)
558         throws MatrixIndexException, MatrixVisitorException {
559         checkSubMatrixIndex(startRow, endRow, startColumn, endColumn);
560         visitor.start(getRowDimension(), getColumnDimension(),
561                       startRow, endRow, startColumn, endColumn);
562         for (int j = startColumn; j <= endColumn; ++j) {
563             for (int i = startRow; i <= endRow; ++i) {
564                 final T[] rowI = data[i];
565                 rowI[j] = visitor.visit(i, j, rowI[j]);
566             }
567         }
568         return visitor.end();
569     }
570 
571     /** {@inheritDoc} */
572     @Override
573     public T walkInColumnOrder(final FieldMatrixPreservingVisitor<T> visitor,
574                                final int startRow, final int endRow,
575                                final int startColumn, final int endColumn)
576         throws MatrixIndexException, MatrixVisitorException {
577         checkSubMatrixIndex(startRow, endRow, startColumn, endColumn);
578         visitor.start(getRowDimension(), getColumnDimension(),
579                       startRow, endRow, startColumn, endColumn);
580         for (int j = startColumn; j <= endColumn; ++j) {
581             for (int i = startRow; i <= endRow; ++i) {
582                 visitor.visit(i, j, data[i][j]);
583             }
584         }
585         return visitor.end();
586     }
587 
588     /**
589      * Returns a fresh copy of the underlying data array.
590      *
591      * @return a copy of the underlying data array.
592      */
593     private T[][] copyOut() {
594         final int nRows = this.getRowDimension();
595         final T[][] out = buildArray(getField(), nRows, getColumnDimension());
596         // can't copy 2-d array in one shot, otherwise get row references
597         for (int i = 0; i < nRows; i++) {
598             System.arraycopy(data[i], 0, out[i], 0, data[i].length);
599         }
600         return out;
601     }
602 
603     /**
604      * Replaces data with a fresh copy of the input array.
605      * <p>
606      * Verifies that the input array is rectangular and non-empty.</p>
607      *
608      * @param in data to copy in
609      * @throws IllegalArgumentException if input array is empty or not
610      *    rectangular
611      * @throws NullPointerException if input array is null
612      */
613     private void copyIn(final T[][] in) {
614         setSubMatrix(in, 0, 0);
615     }
616 
617 }