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    
018    package org.apache.commons.math.linear;
019    
020    import java.io.Serializable;
021    
022    import org.apache.commons.math.Field;
023    import org.apache.commons.math.FieldElement;
024    import org.apache.commons.math.MathRuntimeException;
025    
026    /**
027     * Implementation of FieldMatrix<T> using a {@link FieldElement}[][] array to store entries.
028     * <p>
029     * As specified in the {@link FieldMatrix} interface, matrix element indexing
030     * is 0-based -- e.g., <code>getEntry(0, 0)</code>
031     * returns the element in the first row, first column of the matrix.</li></ul>
032     * </p>
033     *
034     * @param <T> the type of the field elements
035     * @version $Revision: 783702 $ $Date: 2009-06-11 04:54:02 -0400 (Thu, 11 Jun 2009) $
036     */
037    public class Array2DRowFieldMatrix<T extends FieldElement<T>> extends AbstractFieldMatrix<T> implements Serializable {
038        
039        /** Serializable version identifier */
040        private static final long serialVersionUID = 7260756672015356458L;
041    
042        /** Entries of the matrix */
043        protected T[][] data;
044    
045        /**
046         * Creates a matrix with no data
047         * @param field field to which the elements belong
048         */
049        public Array2DRowFieldMatrix(final Field<T> field) {
050            super(field);
051        }
052    
053        /**
054         * Create a new FieldMatrix<T> with the supplied row and column dimensions.
055         *
056         * @param field field to which the elements belong
057         * @param rowDimension  the number of rows in the new matrix
058         * @param columnDimension  the number of columns in the new matrix
059         * @throws IllegalArgumentException if row or column dimension is not
060         *  positive
061         */
062        public Array2DRowFieldMatrix(final Field<T> field,
063                               final int rowDimension, final int columnDimension)
064            throws IllegalArgumentException {
065            super(field, rowDimension, columnDimension);
066            data = buildArray(field, rowDimension, columnDimension);
067        }
068    
069        /**
070         * Create a new FieldMatrix<T> using the input array as the underlying
071         * data array.
072         * <p>The input array is copied, not referenced. This constructor has
073         * the same effect as calling {@link #Array2DRowFieldMatrix(FieldElement[][], boolean)}
074         * with the second argument set to <code>true</code>.</p>
075         *
076         * @param d data for new matrix
077         * @throws IllegalArgumentException if <code>d</code> is not rectangular
078         *  (not all rows have the same length) or empty
079         * @throws NullPointerException if <code>d</code> is null
080         * @see #Array2DRowFieldMatrix(FieldElement[][], boolean)
081         */
082        public Array2DRowFieldMatrix(final T[][] d)
083            throws IllegalArgumentException, NullPointerException {
084            super(extractField(d));
085            copyIn(d);
086        }
087    
088        /**
089         * Create a new FieldMatrix<T> using the input array as the underlying
090         * data array.
091         * <p>If an array is built specially in order to be embedded in a
092         * FieldMatrix<T> and not used directly, the <code>copyArray</code> may be
093         * set to <code>false</code. This will prevent the copying and improve
094         * performance as no new array will be built and no data will be copied.</p>
095         * @param d data for new matrix
096         * @param copyArray if true, the input array will be copied, otherwise
097         * it will be referenced
098         * @throws IllegalArgumentException if <code>d</code> is not rectangular
099         *  (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    }