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    package org.apache.commons.math3.linear;
018    
019    import java.io.Serializable;
020    
021    import org.apache.commons.math3.exception.DimensionMismatchException;
022    import org.apache.commons.math3.exception.NotStrictlyPositiveException;
023    import org.apache.commons.math3.exception.NullArgumentException;
024    import org.apache.commons.math3.exception.NumberIsTooLargeException;
025    import org.apache.commons.math3.exception.OutOfRangeException;
026    import org.apache.commons.math3.util.FastMath;
027    import org.apache.commons.math3.util.MathUtils;
028    import org.apache.commons.math3.util.Precision;
029    
030    /**
031     * Implementation of a diagonal matrix.
032     *
033     * @version $Id$
034     * @since 3.1.1
035     */
036    public class DiagonalMatrix extends AbstractRealMatrix
037        implements Serializable {
038        /** Serializable version identifier. */
039        private static final long serialVersionUID = 20121229L;
040        /** Entries of the diagonal. */
041        private final double[] data;
042    
043        /**
044         * Creates a matrix with the supplied dimension.
045         *
046         * @param dimension Number of rows and columns in the new matrix.
047         * @throws NotStrictlyPositiveException if the dimension is
048         * not positive.
049         */
050        public DiagonalMatrix(final int dimension)
051            throws NotStrictlyPositiveException {
052            super(dimension, dimension);
053            data = new double[dimension];
054        }
055    
056        /**
057         * Creates a matrix using the input array as the underlying data.
058         * <br/>
059         * The input array is copied, not referenced.
060         *
061         * @param d Data for the new matrix.
062         */
063        public DiagonalMatrix(final double[] d) {
064            this(d, true);
065        }
066    
067        /**
068         * Creates a matrix using the input array as the underlying data.
069         * <br/>
070         * If an array is created specially in order to be embedded in a
071         * this instance and not used directly, the {@code copyArray} may be
072         * set to {@code false}.
073         * This will prevent the copying and improve performance as no new
074         * array will be built and no data will be copied.
075         *
076         * @param d Data for new matrix.
077         * @param copyArray if {@code true}, the input array will be copied,
078         * otherwise it will be referenced.
079         * @exception NullArgumentException if d is null
080         */
081        public DiagonalMatrix(final double[] d, final boolean copyArray)
082            throws NullArgumentException {
083            MathUtils.checkNotNull(d);
084            data = copyArray ? d.clone() : d;
085        }
086    
087        /**
088         * {@inheritDoc}
089         *
090         * @throws DimensionMismatchException if the requested dimensions are not equal.
091         */
092        @Override
093        public RealMatrix createMatrix(final int rowDimension,
094                                       final int columnDimension)
095            throws NotStrictlyPositiveException,
096                   DimensionMismatchException {
097            if (rowDimension != columnDimension) {
098                throw new DimensionMismatchException(rowDimension, columnDimension);
099            }
100    
101            return new DiagonalMatrix(rowDimension);
102        }
103    
104        /** {@inheritDoc} */
105        @Override
106        public RealMatrix copy() {
107            return new DiagonalMatrix(data);
108        }
109    
110        /**
111         * Compute the sum of {@code this} and {@code m}.
112         *
113         * @param m Matrix to be added.
114         * @return {@code this + m}.
115         * @throws MatrixDimensionMismatchException if {@code m} is not the same
116         * size as {@code this}.
117         */
118        public DiagonalMatrix add(final DiagonalMatrix m)
119            throws MatrixDimensionMismatchException {
120            // Safety check.
121            MatrixUtils.checkAdditionCompatible(this, m);
122    
123            final int dim = getRowDimension();
124            final double[] outData = new double[dim];
125            for (int i = 0; i < dim; i++) {
126                outData[i] = data[i] + m.data[i];
127            }
128    
129            return new DiagonalMatrix(outData, false);
130        }
131    
132        /**
133         * Returns {@code this} minus {@code m}.
134         *
135         * @param m Matrix to be subtracted.
136         * @return {@code this - m}
137         * @throws MatrixDimensionMismatchException if {@code m} is not the same
138         * size as {@code this}.
139         */
140        public DiagonalMatrix subtract(final DiagonalMatrix m)
141            throws MatrixDimensionMismatchException {
142            MatrixUtils.checkSubtractionCompatible(this, m);
143    
144            final int dim = getRowDimension();
145            final double[] outData = new double[dim];
146            for (int i = 0; i < dim; i++) {
147                outData[i] = data[i] - m.data[i];
148            }
149    
150            return new DiagonalMatrix(outData, false);
151        }
152    
153        /**
154         * Returns the result of postmultiplying {@code this} by {@code m}.
155         *
156         * @param m matrix to postmultiply by
157         * @return {@code this * m}
158         * @throws DimensionMismatchException if
159         * {@code columnDimension(this) != rowDimension(m)}
160         */
161        public DiagonalMatrix multiply(final DiagonalMatrix m)
162            throws DimensionMismatchException {
163            MatrixUtils.checkMultiplicationCompatible(this, m);
164    
165            final int dim = getRowDimension();
166            final double[] outData = new double[dim];
167            for (int i = 0; i < dim; i++) {
168                outData[i] = data[i] * m.data[i];
169            }
170    
171            return new DiagonalMatrix(outData, false);
172        }
173    
174        /**
175         * Returns the result of postmultiplying {@code this} by {@code m}.
176         *
177         * @param m matrix to postmultiply by
178         * @return {@code this * m}
179         * @throws DimensionMismatchException if
180         * {@code columnDimension(this) != rowDimension(m)}
181         */
182        public RealMatrix multiply(final RealMatrix m)
183            throws DimensionMismatchException {
184            if (m instanceof DiagonalMatrix) {
185                return multiply((DiagonalMatrix) m);
186            } else {
187                MatrixUtils.checkMultiplicationCompatible(this, m);
188                final int nRows = m.getRowDimension();
189                final int nCols = m.getColumnDimension();
190                final double[][] product = new double[nRows][nCols];
191                for (int r = 0; r < nRows; r++) {
192                    for (int c = 0; c < nCols; c++) {
193                        product[r][c] = data[r] * m.getEntry(r, c);
194                    }
195                }
196                return new Array2DRowRealMatrix(product, false);
197            }
198        }
199    
200        /** {@inheritDoc} */
201        @Override
202        public double[][] getData() {
203            final int dim = getRowDimension();
204            final double[][] out = new double[dim][dim];
205    
206            for (int i = 0; i < dim; i++) {
207                out[i][i] = data[i];
208            }
209    
210            return out;
211        }
212    
213        /**
214         * Gets a reference to the underlying data array.
215         *
216         * @return 1-dimensional array of entries.
217         */
218        public double[] getDataRef() {
219            return data;
220        }
221    
222        /** {@inheritDoc} */
223        @Override
224        public double getEntry(final int row, final int column)
225            throws OutOfRangeException {
226            MatrixUtils.checkMatrixIndex(this, row, column);
227            return row == column ? data[row] : 0;
228        }
229    
230        /** {@inheritDoc}
231         * @throws NumberIsTooLargeException if {@code row != column} and value is non-zero.
232         */
233        @Override
234        public void setEntry(final int row, final int column, final double value)
235            throws OutOfRangeException, NumberIsTooLargeException {
236            if (row == column) {
237                MatrixUtils.checkRowIndex(this, row);
238                data[row] = value;
239            } else {
240                ensureZero(value);
241            }
242        }
243    
244        /** {@inheritDoc}
245         * @throws NumberIsTooLargeException if {@code row != column} and increment is non-zero.
246         */
247        @Override
248        public void addToEntry(final int row,
249                               final int column,
250                               final double increment)
251            throws OutOfRangeException, NumberIsTooLargeException {
252            if (row == column) {
253                MatrixUtils.checkRowIndex(this, row);
254                data[row] += increment;
255            } else {
256                ensureZero(increment);
257            }
258        }
259    
260        /** {@inheritDoc} */
261        @Override
262        public void multiplyEntry(final int row,
263                                  final int column,
264                                  final double factor)
265            throws OutOfRangeException {
266            // we don't care about non-diagonal elements for multiplication
267            if (row == column) {
268                MatrixUtils.checkRowIndex(this, row);
269                data[row] *= factor;
270            }
271        }
272    
273        /** {@inheritDoc} */
274        @Override
275        public int getRowDimension() {
276            return data.length;
277        }
278    
279        /** {@inheritDoc} */
280        @Override
281        public int getColumnDimension() {
282            return data.length;
283        }
284    
285        /** {@inheritDoc} */
286        @Override
287        public double[] operate(final double[] v)
288            throws DimensionMismatchException {
289            return multiply(new DiagonalMatrix(v, false)).getDataRef();
290        }
291    
292        /** {@inheritDoc} */
293        @Override
294        public double[] preMultiply(final double[] v)
295            throws DimensionMismatchException {
296            return operate(v);
297        }
298    
299        /** Ensure a value is zero.
300         * @param value value to check
301         * @exception NumberIsTooLargeException if value is not zero
302         */
303        private void ensureZero(final double value) throws NumberIsTooLargeException {
304            if (!Precision.equals(0.0, value, 1)) {
305                throw new NumberIsTooLargeException(FastMath.abs(value), 0, true);
306            }
307        }
308    
309    }