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    import org.apache.commons.math.linear.MatrixVisitorException;
026    import org.apache.commons.math.exception.util.LocalizedFormats;
027    
028    /**
029     * Implementation of FieldMatrix<T> using a {@link FieldElement}[][] array to store entries.
030     * <p>
031     * As specified in the {@link FieldMatrix} interface, matrix element indexing
032     * is 0-based -- e.g., <code>getEntry(0, 0)</code>
033     * returns the element in the first row, first column of the matrix.</li></ul>
034     * </p>
035     *
036     * @param <T> the type of the field elements
037     * @version $Revision: 1073158 $ $Date: 2011-02-21 22:46:52 +0100 (lun. 21 f??vr. 2011) $
038     */
039    public class Array2DRowFieldMatrix<T extends FieldElement<T>> extends AbstractFieldMatrix<T> implements Serializable {
040    
041        /** Serializable version identifier */
042        private static final long serialVersionUID = 7260756672015356458L;
043    
044        /** Entries of the matrix */
045        protected T[][] data;
046    
047        /**
048         * Creates a matrix with no data
049         * @param field field to which the elements belong
050         */
051        public Array2DRowFieldMatrix(final Field<T> field) {
052            super(field);
053        }
054    
055        /**
056         * Create a new FieldMatrix<T> with the supplied row and column dimensions.
057         *
058         * @param field field to which the elements belong
059         * @param rowDimension  the number of rows in the new matrix
060         * @param columnDimension  the number of columns in the new matrix
061         * @throws IllegalArgumentException if row or column dimension is not
062         *  positive
063         */
064        public Array2DRowFieldMatrix(final Field<T> field,
065                               final int rowDimension, final int columnDimension)
066            throws IllegalArgumentException {
067            super(field, rowDimension, columnDimension);
068            data = buildArray(field, rowDimension, columnDimension);
069        }
070    
071        /**
072         * Create a new FieldMatrix<T> using the input array as the underlying
073         * data array.
074         * <p>The input array is copied, not referenced. This constructor has
075         * the same effect as calling {@link #Array2DRowFieldMatrix(FieldElement[][], boolean)}
076         * with the second argument set to <code>true</code>.</p>
077         *
078         * @param d data for new matrix
079         * @throws IllegalArgumentException if <code>d</code> is not rectangular
080         *  (not all rows have the same length) or empty
081         * @throws NullPointerException if <code>d</code> is null
082         * @see #Array2DRowFieldMatrix(FieldElement[][], boolean)
083         */
084        public Array2DRowFieldMatrix(final T[][] d)
085            throws IllegalArgumentException, NullPointerException {
086            super(extractField(d));
087            copyIn(d);
088        }
089    
090        /**
091         * Create a new FieldMatrix<T> using the input array as the underlying
092         * data array.
093         * <p>If an array is built specially in order to be embedded in a
094         * FieldMatrix<T> and not used directly, the <code>copyArray</code> may be
095         * set to <code>false</code. This will prevent the copying and improve
096         * performance as no new array will be built and no data will be copied.</p>
097         * @param d data for new matrix
098         * @param copyArray if true, the input array will be copied, otherwise
099         * it will be referenced
100         * @throws IllegalArgumentException if <code>d</code> is not rectangular
101         *  (not all rows have the same length) or empty
102         * @throws NullPointerException if <code>d</code> is null
103         * @see #Array2DRowFieldMatrix(FieldElement[][])
104         */
105        public Array2DRowFieldMatrix(final T[][] d, final boolean copyArray)
106            throws IllegalArgumentException, NullPointerException {
107            super(extractField(d));
108            if (copyArray) {
109                copyIn(d);
110            } else {
111                if (d == null) {
112                    throw new NullPointerException();
113                }
114                final int nRows = d.length;
115                if (nRows == 0) {
116                    throw MathRuntimeException.createIllegalArgumentException(
117                          LocalizedFormats.AT_LEAST_ONE_ROW);
118                }
119                final int nCols = d[0].length;
120                if (nCols == 0) {
121                    throw MathRuntimeException.createIllegalArgumentException(
122                          LocalizedFormats.AT_LEAST_ONE_COLUMN);
123                }
124                for (int r = 1; r < nRows; r++) {
125                    if (d[r].length != nCols) {
126                        throw MathRuntimeException.createIllegalArgumentException(
127                              LocalizedFormats.DIFFERENT_ROWS_LENGTHS, nCols, d[r].length);
128                    }
129                }
130                data = d;
131            }
132        }
133    
134        /**
135         * Create a new (column) FieldMatrix<T> using <code>v</code> as the
136         * data for the unique column of the <code>v.length x 1</code> matrix
137         * created.
138         * <p>The input array is copied, not referenced.</p>
139         *
140         * @param v column vector holding data for new matrix
141         */
142        public Array2DRowFieldMatrix(final T[] v) {
143            super(extractField(v));
144            final int nRows = v.length;
145            data = buildArray(getField(), nRows, 1);
146            for (int row = 0; row < nRows; row++) {
147                data[row][0] = v[row];
148            }
149        }
150    
151        /** {@inheritDoc} */
152        @Override
153        public FieldMatrix<T> createMatrix(final int rowDimension, final int columnDimension)
154            throws IllegalArgumentException {
155            return new Array2DRowFieldMatrix<T>(getField(), rowDimension, columnDimension);
156        }
157    
158        /** {@inheritDoc} */
159        @Override
160        public FieldMatrix<T> copy() {
161            return new Array2DRowFieldMatrix<T>(copyOut(), false);
162        }
163    
164        /** {@inheritDoc} */
165        @Override
166        public FieldMatrix<T> add(final FieldMatrix<T> m)
167            throws IllegalArgumentException {
168            try {
169                return add((Array2DRowFieldMatrix<T>) m);
170            } catch (ClassCastException cce) {
171                return super.add(m);
172            }
173        }
174    
175        /**
176         * Compute the sum of this and <code>m</code>.
177         *
178         * @param m    matrix to be added
179         * @return     this + m
180         * @throws  IllegalArgumentException if m is not the same size as this
181         */
182        public Array2DRowFieldMatrix<T> add(final Array2DRowFieldMatrix<T> m)
183            throws IllegalArgumentException {
184    
185            // safety check
186            checkAdditionCompatible(m);
187    
188            final int rowCount    = getRowDimension();
189            final int columnCount = getColumnDimension();
190            final T[][] outData = buildArray(getField(), rowCount, columnCount);
191            for (int row = 0; row < rowCount; row++) {
192                final T[] dataRow    = data[row];
193                final T[] mRow       = m.data[row];
194                final T[] outDataRow = outData[row];
195                for (int col = 0; col < columnCount; col++) {
196                    outDataRow[col] = dataRow[col].add(mRow[col]);
197                }
198            }
199    
200            return new Array2DRowFieldMatrix<T>(outData, false);
201    
202        }
203    
204        /** {@inheritDoc} */
205        @Override
206        public FieldMatrix<T> subtract(final FieldMatrix<T> m)
207            throws IllegalArgumentException {
208            try {
209                return subtract((Array2DRowFieldMatrix<T>) m);
210            } catch (ClassCastException cce) {
211                return super.subtract(m);
212            }
213        }
214    
215        /**
216         * Compute  this minus <code>m</code>.
217         *
218         * @param m    matrix to be subtracted
219         * @return     this + m
220         * @throws  IllegalArgumentException if m is not the same size as this
221         */
222        public Array2DRowFieldMatrix<T> subtract(final Array2DRowFieldMatrix<T> m)
223            throws IllegalArgumentException {
224    
225            // safety check
226            checkSubtractionCompatible(m);
227    
228            final int rowCount    = getRowDimension();
229            final int columnCount = getColumnDimension();
230            final T[][] outData = buildArray(getField(), rowCount, columnCount);
231            for (int row = 0; row < rowCount; row++) {
232                final T[] dataRow    = data[row];
233                final T[] mRow       = m.data[row];
234                final T[] outDataRow = outData[row];
235                for (int col = 0; col < columnCount; col++) {
236                    outDataRow[col] = dataRow[col].subtract(mRow[col]);
237                }
238            }
239    
240            return new Array2DRowFieldMatrix<T>(outData, false);
241    
242        }
243    
244        /** {@inheritDoc} */
245        @Override
246        public FieldMatrix<T> multiply(final FieldMatrix<T> m)
247            throws IllegalArgumentException {
248            try {
249                return multiply((Array2DRowFieldMatrix<T>) m);
250            } catch (ClassCastException cce) {
251                return super.multiply(m);
252            }
253        }
254    
255        /**
256         * Returns the result of postmultiplying this by <code>m</code>.
257         * @param m    matrix to postmultiply by
258         * @return     this*m
259         * @throws     IllegalArgumentException
260         *             if columnDimension(this) != rowDimension(m)
261         */
262        public Array2DRowFieldMatrix<T> multiply(final Array2DRowFieldMatrix<T> m)
263            throws IllegalArgumentException {
264    
265            // safety check
266            checkMultiplicationCompatible(m);
267    
268            final int nRows = this.getRowDimension();
269            final int nCols = m.getColumnDimension();
270            final int nSum = this.getColumnDimension();
271            final T[][] outData = buildArray(getField(), nRows, nCols);
272            for (int row = 0; row < nRows; row++) {
273                final T[] dataRow    = data[row];
274                final T[] outDataRow = outData[row];
275                for (int col = 0; col < nCols; col++) {
276                    T sum = getField().getZero();
277                    for (int i = 0; i < nSum; i++) {
278                        sum = sum.add(dataRow[i].multiply(m.data[i][col]));
279                    }
280                    outDataRow[col] = sum;
281                }
282            }
283    
284            return new Array2DRowFieldMatrix<T>(outData, false);
285    
286        }
287    
288        /** {@inheritDoc} */
289        @Override
290        public T[][] getData() {
291            return copyOut();
292        }
293    
294        /**
295         * Returns a reference to the underlying data array.
296         * <p>
297         * Does <strong>not</strong> make a fresh copy of the underlying data.</p>
298         *
299         * @return 2-dimensional array of entries
300         */
301        public T[][] getDataRef() {
302            return data;
303        }
304    
305        /** {@inheritDoc} */
306        @Override
307        public void setSubMatrix(final T[][] subMatrix, final int row, final int column)
308        throws MatrixIndexException {
309            if (data == null) {
310                if (row > 0) {
311                    throw MathRuntimeException.createIllegalStateException(
312                          LocalizedFormats.FIRST_ROWS_NOT_INITIALIZED_YET, row);
313                }
314                if (column > 0) {
315                    throw MathRuntimeException.createIllegalStateException(
316                          LocalizedFormats.FIRST_COLUMNS_NOT_INITIALIZED_YET, column);
317                }
318                final int nRows = subMatrix.length;
319                if (nRows == 0) {
320                    throw MathRuntimeException.createIllegalArgumentException(
321                          LocalizedFormats.AT_LEAST_ONE_ROW);
322                }
323    
324                final int nCols = subMatrix[0].length;
325                if (nCols == 0) {
326                    throw MathRuntimeException.createIllegalArgumentException(
327                          LocalizedFormats.AT_LEAST_ONE_COLUMN);
328                }
329                data = buildArray(getField(), subMatrix.length, nCols);
330                for (int i = 0; i < data.length; ++i) {
331                    if (subMatrix[i].length != nCols) {
332                        throw MathRuntimeException.createIllegalArgumentException(
333                              LocalizedFormats.DIFFERENT_ROWS_LENGTHS, nCols, subMatrix[i].length);
334                    }
335                    System.arraycopy(subMatrix[i], 0, data[i + row], column, nCols);
336                }
337            } else {
338                super.setSubMatrix(subMatrix, row, column);
339            }
340    
341        }
342    
343        /** {@inheritDoc} */
344        @Override
345        public T getEntry(final int row, final int column)
346            throws MatrixIndexException {
347            try {
348                return data[row][column];
349            } catch (ArrayIndexOutOfBoundsException e) {
350                throw new MatrixIndexException(
351                          LocalizedFormats.NO_SUCH_MATRIX_ENTRY, row, column, getRowDimension(), getColumnDimension());
352            }
353        }
354    
355        /** {@inheritDoc} */
356        @Override
357        public void setEntry(final int row, final int column, final T value)
358            throws MatrixIndexException {
359            try {
360                data[row][column] = value;
361            } catch (ArrayIndexOutOfBoundsException e) {
362                throw new MatrixIndexException(
363                          LocalizedFormats.NO_SUCH_MATRIX_ENTRY, 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                          LocalizedFormats.NO_SUCH_MATRIX_ENTRY, row, column, getRowDimension(), getColumnDimension());
376            }
377        }
378    
379        /** {@inheritDoc} */
380        @Override
381        public void multiplyEntry(final int row, final int column, final T factor)
382            throws MatrixIndexException {
383            try {
384                data[row][column] = data[row][column].multiply(factor);
385            } catch (ArrayIndexOutOfBoundsException e) {
386                throw new MatrixIndexException(
387                          LocalizedFormats.NO_SUCH_MATRIX_ENTRY, row, column, getRowDimension(), getColumnDimension());
388            }
389        }
390    
391        /** {@inheritDoc} */
392        @Override
393        public int getRowDimension() {
394            return (data == null) ? 0 : data.length;
395        }
396    
397        /** {@inheritDoc} */
398        @Override
399        public int getColumnDimension() {
400            return ((data == null) || (data[0] == null)) ? 0 : data[0].length;
401        }
402    
403        /** {@inheritDoc} */
404        @Override
405        public T[] operate(final T[] v)
406            throws IllegalArgumentException {
407            final int nRows = this.getRowDimension();
408            final int nCols = this.getColumnDimension();
409            if (v.length != nCols) {
410                throw MathRuntimeException.createIllegalArgumentException(
411                      LocalizedFormats.VECTOR_LENGTH_MISMATCH, v.length, nCols);
412            }
413            final T[] out = buildArray(getField(), nRows);
414            for (int row = 0; row < nRows; row++) {
415                final T[] dataRow = data[row];
416                T sum = getField().getZero();
417                for (int i = 0; i < nCols; i++) {
418                    sum = sum.add(dataRow[i].multiply(v[i]));
419                }
420                out[row] = sum;
421            }
422            return out;
423        }
424    
425        /** {@inheritDoc} */
426        @Override
427        public T[] preMultiply(final T[] v)
428            throws IllegalArgumentException {
429    
430            final int nRows = getRowDimension();
431            final int nCols = getColumnDimension();
432            if (v.length != nRows) {
433                throw MathRuntimeException.createIllegalArgumentException(
434                      LocalizedFormats.VECTOR_LENGTH_MISMATCH, v.length, nRows);
435            }
436    
437            final T[] out = buildArray(getField(), nCols);
438            for (int col = 0; col < nCols; ++col) {
439                T sum = getField().getZero();
440                for (int i = 0; i < nRows; ++i) {
441                    sum = sum.add(data[i][col].multiply(v[i]));
442                }
443                out[col] = sum;
444            }
445    
446            return out;
447    
448        }
449    
450        /** {@inheritDoc} */
451        @Override
452        public T walkInRowOrder(final FieldMatrixChangingVisitor<T> visitor)
453            throws MatrixVisitorException {
454            final int rows    = getRowDimension();
455            final int columns = getColumnDimension();
456            visitor.start(rows, columns, 0, rows - 1, 0, columns - 1);
457            for (int i = 0; i < rows; ++i) {
458                final T[] rowI = data[i];
459                for (int j = 0; j < columns; ++j) {
460                    rowI[j] = visitor.visit(i, j, rowI[j]);
461                }
462            }
463            return visitor.end();
464        }
465    
466        /** {@inheritDoc} */
467        @Override
468        public T walkInRowOrder(final FieldMatrixPreservingVisitor<T> visitor)
469            throws MatrixVisitorException {
470            final int rows    = getRowDimension();
471            final int columns = getColumnDimension();
472            visitor.start(rows, columns, 0, rows - 1, 0, columns - 1);
473            for (int i = 0; i < rows; ++i) {
474                final T[] rowI = data[i];
475                for (int j = 0; j < columns; ++j) {
476                    visitor.visit(i, j, rowI[j]);
477                }
478            }
479            return visitor.end();
480        }
481    
482        /** {@inheritDoc} */
483        @Override
484        public T walkInRowOrder(final FieldMatrixChangingVisitor<T> visitor,
485                                final int startRow, final int endRow,
486                                final int startColumn, final int endColumn)
487            throws MatrixIndexException, MatrixVisitorException {
488            checkSubMatrixIndex(startRow, endRow, startColumn, endColumn);
489            visitor.start(getRowDimension(), getColumnDimension(),
490                          startRow, endRow, startColumn, endColumn);
491            for (int i = startRow; i <= endRow; ++i) {
492                final T[] rowI = data[i];
493                for (int j = startColumn; j <= endColumn; ++j) {
494                    rowI[j] = visitor.visit(i, j, rowI[j]);
495                }
496            }
497            return visitor.end();
498        }
499    
500        /** {@inheritDoc} */
501        @Override
502        public T walkInRowOrder(final FieldMatrixPreservingVisitor<T> visitor,
503                                final int startRow, final int endRow,
504                                final int startColumn, final int endColumn)
505            throws MatrixIndexException, MatrixVisitorException {
506            checkSubMatrixIndex(startRow, endRow, startColumn, endColumn);
507            visitor.start(getRowDimension(), getColumnDimension(),
508                          startRow, endRow, startColumn, endColumn);
509            for (int i = startRow; i <= endRow; ++i) {
510                final T[] rowI = data[i];
511                for (int j = startColumn; j <= endColumn; ++j) {
512                    visitor.visit(i, j, rowI[j]);
513                }
514            }
515            return visitor.end();
516        }
517    
518        /** {@inheritDoc} */
519        @Override
520        public T walkInColumnOrder(final FieldMatrixChangingVisitor<T> visitor)
521            throws MatrixVisitorException {
522            final int rows    = getRowDimension();
523            final int columns = getColumnDimension();
524            visitor.start(rows, columns, 0, rows - 1, 0, columns - 1);
525            for (int j = 0; j < columns; ++j) {
526                for (int i = 0; i < rows; ++i) {
527                    final T[] rowI = data[i];
528                    rowI[j] = visitor.visit(i, j, rowI[j]);
529                }
530            }
531            return visitor.end();
532        }
533    
534        /** {@inheritDoc} */
535        @Override
536        public T walkInColumnOrder(final FieldMatrixPreservingVisitor<T> visitor)
537            throws MatrixVisitorException {
538            final int rows    = getRowDimension();
539            final int columns = getColumnDimension();
540            visitor.start(rows, columns, 0, rows - 1, 0, columns - 1);
541            for (int j = 0; j < columns; ++j) {
542                for (int i = 0; i < rows; ++i) {
543                    visitor.visit(i, j, data[i][j]);
544                }
545            }
546            return visitor.end();
547        }
548    
549        /** {@inheritDoc} */
550        @Override
551        public T walkInColumnOrder(final FieldMatrixChangingVisitor<T> visitor,
552                                   final int startRow, final int endRow,
553                                   final int startColumn, final int endColumn)
554            throws MatrixIndexException, MatrixVisitorException {
555            checkSubMatrixIndex(startRow, endRow, startColumn, endColumn);
556            visitor.start(getRowDimension(), getColumnDimension(),
557                          startRow, endRow, startColumn, endColumn);
558            for (int j = startColumn; j <= endColumn; ++j) {
559                for (int i = startRow; i <= endRow; ++i) {
560                    final T[] rowI = data[i];
561                    rowI[j] = visitor.visit(i, j, rowI[j]);
562                }
563            }
564            return visitor.end();
565        }
566    
567        /** {@inheritDoc} */
568        @Override
569        public T walkInColumnOrder(final FieldMatrixPreservingVisitor<T> visitor,
570                                   final int startRow, final int endRow,
571                                   final int startColumn, final int endColumn)
572            throws MatrixIndexException, MatrixVisitorException {
573            checkSubMatrixIndex(startRow, endRow, startColumn, endColumn);
574            visitor.start(getRowDimension(), getColumnDimension(),
575                          startRow, endRow, startColumn, endColumn);
576            for (int j = startColumn; j <= endColumn; ++j) {
577                for (int i = startRow; i <= endRow; ++i) {
578                    visitor.visit(i, j, data[i][j]);
579                }
580            }
581            return visitor.end();
582        }
583    
584        /**
585         * Returns a fresh copy of the underlying data array.
586         *
587         * @return a copy of the underlying data array.
588         */
589        private T[][] copyOut() {
590            final int nRows = this.getRowDimension();
591            final T[][] out = buildArray(getField(), nRows, getColumnDimension());
592            // can't copy 2-d array in one shot, otherwise get row references
593            for (int i = 0; i < nRows; i++) {
594                System.arraycopy(data[i], 0, out[i], 0, data[i].length);
595            }
596            return out;
597        }
598    
599        /**
600         * Replaces data with a fresh copy of the input array.
601         * <p>
602         * Verifies that the input array is rectangular and non-empty.</p>
603         *
604         * @param in data to copy in
605         * @throws IllegalArgumentException if input array is empty or not
606         *    rectangular
607         * @throws NullPointerException if input array is null
608         */
609        private void copyIn(final T[][] in) {
610            setSubMatrix(in, 0, 0);
611        }
612    
613    }