View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.math3.linear;
18  
19  import java.io.Serializable;
20  
21  import org.apache.commons.math3.exception.DimensionMismatchException;
22  import org.apache.commons.math3.exception.NotStrictlyPositiveException;
23  import org.apache.commons.math3.exception.NullArgumentException;
24  import org.apache.commons.math3.exception.NumberIsTooLargeException;
25  import org.apache.commons.math3.exception.OutOfRangeException;
26  import org.apache.commons.math3.util.FastMath;
27  import org.apache.commons.math3.util.MathUtils;
28  import org.apache.commons.math3.util.Precision;
29  
30  /**
31   * Implementation of a diagonal matrix.
32   *
33   * @version $Id: DiagonalMatrix.java 1533638 2013-10-18 21:19:18Z tn $
34   * @since 3.1.1
35   */
36  public class DiagonalMatrix extends AbstractRealMatrix
37      implements Serializable {
38      /** Serializable version identifier. */
39      private static final long serialVersionUID = 20121229L;
40      /** Entries of the diagonal. */
41      private final double[] data;
42  
43      /**
44       * Creates a matrix with the supplied dimension.
45       *
46       * @param dimension Number of rows and columns in the new matrix.
47       * @throws NotStrictlyPositiveException if the dimension is
48       * not positive.
49       */
50      public DiagonalMatrix(final int dimension)
51          throws NotStrictlyPositiveException {
52          super(dimension, dimension);
53          data = new double[dimension];
54      }
55  
56      /**
57       * Creates a matrix using the input array as the underlying data.
58       * <br/>
59       * The input array is copied, not referenced.
60       *
61       * @param d Data for the new matrix.
62       */
63      public DiagonalMatrix(final double[] d) {
64          this(d, true);
65      }
66  
67      /**
68       * Creates a matrix using the input array as the underlying data.
69       * <br/>
70       * If an array is created specially in order to be embedded in a
71       * this instance and not used directly, the {@code copyArray} may be
72       * set to {@code false}.
73       * This will prevent the copying and improve performance as no new
74       * array will be built and no data will be copied.
75       *
76       * @param d Data for new matrix.
77       * @param copyArray if {@code true}, the input array will be copied,
78       * otherwise it will be referenced.
79       * @exception NullArgumentException if d is null
80       */
81      public DiagonalMatrix(final double[] d, final boolean copyArray)
82          throws NullArgumentException {
83          MathUtils.checkNotNull(d);
84          data = copyArray ? d.clone() : d;
85      }
86  
87      /**
88       * {@inheritDoc}
89       *
90       * @throws DimensionMismatchException if the requested dimensions are not equal.
91       */
92      @Override
93      public RealMatrix createMatrix(final int rowDimension,
94                                     final int columnDimension)
95          throws NotStrictlyPositiveException,
96                 DimensionMismatchException {
97          if (rowDimension != columnDimension) {
98              throw new DimensionMismatchException(rowDimension, columnDimension);
99          }
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     @Override
183     public RealMatrix multiply(final RealMatrix m)
184         throws DimensionMismatchException {
185         if (m instanceof DiagonalMatrix) {
186             return multiply((DiagonalMatrix) m);
187         } else {
188             MatrixUtils.checkMultiplicationCompatible(this, m);
189             final int nRows = m.getRowDimension();
190             final int nCols = m.getColumnDimension();
191             final double[][] product = new double[nRows][nCols];
192             for (int r = 0; r < nRows; r++) {
193                 for (int c = 0; c < nCols; c++) {
194                     product[r][c] = data[r] * m.getEntry(r, c);
195                 }
196             }
197             return new Array2DRowRealMatrix(product, false);
198         }
199     }
200 
201     /** {@inheritDoc} */
202     @Override
203     public double[][] getData() {
204         final int dim = getRowDimension();
205         final double[][] out = new double[dim][dim];
206 
207         for (int i = 0; i < dim; i++) {
208             out[i][i] = data[i];
209         }
210 
211         return out;
212     }
213 
214     /**
215      * Gets a reference to the underlying data array.
216      *
217      * @return 1-dimensional array of entries.
218      */
219     public double[] getDataRef() {
220         return data;
221     }
222 
223     /** {@inheritDoc} */
224     @Override
225     public double getEntry(final int row, final int column)
226         throws OutOfRangeException {
227         MatrixUtils.checkMatrixIndex(this, row, column);
228         return row == column ? data[row] : 0;
229     }
230 
231     /** {@inheritDoc}
232      * @throws NumberIsTooLargeException if {@code row != column} and value is non-zero.
233      */
234     @Override
235     public void setEntry(final int row, final int column, final double value)
236         throws OutOfRangeException, NumberIsTooLargeException {
237         if (row == column) {
238             MatrixUtils.checkRowIndex(this, row);
239             data[row] = value;
240         } else {
241             ensureZero(value);
242         }
243     }
244 
245     /** {@inheritDoc}
246      * @throws NumberIsTooLargeException if {@code row != column} and increment is non-zero.
247      */
248     @Override
249     public void addToEntry(final int row,
250                            final int column,
251                            final double increment)
252         throws OutOfRangeException, NumberIsTooLargeException {
253         if (row == column) {
254             MatrixUtils.checkRowIndex(this, row);
255             data[row] += increment;
256         } else {
257             ensureZero(increment);
258         }
259     }
260 
261     /** {@inheritDoc} */
262     @Override
263     public void multiplyEntry(final int row,
264                               final int column,
265                               final double factor)
266         throws OutOfRangeException {
267         // we don't care about non-diagonal elements for multiplication
268         if (row == column) {
269             MatrixUtils.checkRowIndex(this, row);
270             data[row] *= factor;
271         }
272     }
273 
274     /** {@inheritDoc} */
275     @Override
276     public int getRowDimension() {
277         return data.length;
278     }
279 
280     /** {@inheritDoc} */
281     @Override
282     public int getColumnDimension() {
283         return data.length;
284     }
285 
286     /** {@inheritDoc} */
287     @Override
288     public double[] operate(final double[] v)
289         throws DimensionMismatchException {
290         return multiply(new DiagonalMatrix(v, false)).getDataRef();
291     }
292 
293     /** {@inheritDoc} */
294     @Override
295     public double[] preMultiply(final double[] v)
296         throws DimensionMismatchException {
297         return operate(v);
298     }
299 
300     /** {@inheritDoc} */
301     @Override
302     public RealVector preMultiply(final RealVector v) throws DimensionMismatchException {
303         final double[] vectorData;
304         if (v instanceof ArrayRealVector) {
305             vectorData = ((ArrayRealVector) v).getDataRef();
306         } else {
307             vectorData = v.toArray();
308         }
309         return MatrixUtils.createRealVector(preMultiply(vectorData));
310     }
311 
312     /** Ensure a value is zero.
313      * @param value value to check
314      * @exception NumberIsTooLargeException if value is not zero
315      */
316     private void ensureZero(final double value) throws NumberIsTooLargeException {
317         if (!Precision.equals(0.0, value, 1)) {
318             throw new NumberIsTooLargeException(FastMath.abs(value), 0, true);
319         }
320     }
321 
322     /**
323      * Computes the inverse of this diagonal matrix.
324      * <p>
325      * Note: this method will use a singularity threshold of 0,
326      * use {@link #inverse(double)} if a different threshold is needed.
327      *
328      * @return the inverse of {@code m}
329      * @throws SingularMatrixException if the matrix is singular
330      * @since 3.3
331      */
332     public DiagonalMatrix inverse() throws SingularMatrixException {
333         return inverse(0);
334     }
335 
336     /**
337      * Computes the inverse of this diagonal matrix.
338      *
339      * @param threshold Singularity threshold.
340      * @return the inverse of {@code m}
341      * @throws SingularMatrixException if the matrix is singular
342      * @since 3.3
343      */
344     public DiagonalMatrix inverse(double threshold) throws SingularMatrixException {
345         if (isSingular(threshold)) {
346             throw new SingularMatrixException();
347         }
348 
349         final double[] result = new double[data.length];
350         for (int i = 0; i < data.length; i++) {
351             result[i] = 1.0 / data[i];
352         }
353         return new DiagonalMatrix(result, false);
354     }
355 
356     /** Returns whether this diagonal matrix is singular, i.e. any diagonal entry
357      * is equal to {@code 0} within the given threshold.
358      *
359      * @param threshold Singularity threshold.
360      * @return {@code true} if the matrix is singular, {@code false} otherwise
361      * @since 3.3
362      */
363     public boolean isSingular(double threshold) {
364         for (int i = 0; i < data.length; i++) {
365             if (Precision.equals(data[i], 0.0, threshold)) {
366                 return true;
367             }
368         }
369         return false;
370     }
371 }