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