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    *      https://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  
18  /*
19   * This is not the original file distributed by the Apache Software Foundation
20   * It has been modified by the Hipparchus project
21   */
22  package org.hipparchus.linear;
23  
24  import java.io.Serializable;
25  
26  import org.hipparchus.exception.LocalizedCoreFormats;
27  import org.hipparchus.exception.MathIllegalArgumentException;
28  import org.hipparchus.exception.NullArgumentException;
29  import org.hipparchus.util.FastMath;
30  import org.hipparchus.util.MathUtils;
31  import org.hipparchus.util.Precision;
32  
33  /**
34   * Implementation of a diagonal matrix.
35   *
36   */
37  public class DiagonalMatrix extends AbstractRealMatrix
38      implements Serializable {
39      /** Serializable version identifier. */
40      private static final long serialVersionUID = 20121229L;
41      /** Entries of the diagonal. */
42      private final double[] data;
43  
44      /**
45       * Creates a matrix with the supplied dimension.
46       *
47       * @param dimension Number of rows and columns in the new matrix.
48       * @throws MathIllegalArgumentException if the dimension is
49       * not positive.
50       */
51      public DiagonalMatrix(final int dimension)
52          throws MathIllegalArgumentException {
53          super(dimension, dimension);
54          data = new double[dimension];
55      }
56  
57      /**
58       * Creates a matrix using the input array as the underlying data.
59       * <br>
60       * The input array is copied, not referenced.
61       *
62       * @param d Data for the new matrix.
63       */
64      public DiagonalMatrix(final double[] d) {
65          this(d, true);
66      }
67  
68      /**
69       * Creates a matrix using the input array as the underlying data.
70       * <br>
71       * If an array is created specially in order to be embedded in a
72       * this instance and not used directly, the {@code copyArray} may be
73       * set to {@code false}.
74       * This will prevent the copying and improve performance as no new
75       * array will be built and no data will be copied.
76       *
77       * @param d Data for new matrix.
78       * @param copyArray if {@code true}, the input array will be copied,
79       * otherwise it will be referenced.
80       * @exception NullArgumentException if d is null
81       */
82      public DiagonalMatrix(final double[] d, final boolean copyArray)
83          throws NullArgumentException {
84          MathUtils.checkNotNull(d);
85          data = copyArray ? d.clone() : d;
86      }
87  
88      /**
89       * {@inheritDoc}
90       *
91       * @throws MathIllegalArgumentException if the requested dimensions are not equal.
92       */
93      @Override
94      public RealMatrix createMatrix(final int rowDimension,
95                                     final int columnDimension)
96          throws MathIllegalArgumentException {
97          if (rowDimension != columnDimension) {
98              throw new MathIllegalArgumentException(LocalizedCoreFormats.DIMENSIONS_MISMATCH,
99                                                     rowDimension, columnDimension);
100         }
101 
102         return new DiagonalMatrix(rowDimension);
103     }
104 
105     /** {@inheritDoc} */
106     @Override
107     public RealMatrix copy() {
108         return new DiagonalMatrix(data);
109     }
110 
111     /**
112      * Compute the sum of {@code this} and {@code m}.
113      *
114      * @param m Matrix to be added.
115      * @return {@code this + m}.
116      * @throws MathIllegalArgumentException if {@code m} is not the same
117      * size as {@code this}.
118      */
119     public DiagonalMatrix add(final DiagonalMatrix m)
120         throws MathIllegalArgumentException {
121         // Safety check.
122         MatrixUtils.checkAdditionCompatible(this, m);
123 
124         final int dim = getRowDimension();
125         final double[] outData = new double[dim];
126         for (int i = 0; i < dim; i++) {
127             outData[i] = data[i] + m.data[i];
128         }
129 
130         return new DiagonalMatrix(outData, false);
131     }
132 
133     /**
134      * Returns {@code this} minus {@code m}.
135      *
136      * @param m Matrix to be subtracted.
137      * @return {@code this - m}
138      * @throws MathIllegalArgumentException if {@code m} is not the same
139      * size as {@code this}.
140      */
141     public DiagonalMatrix subtract(final DiagonalMatrix m)
142         throws MathIllegalArgumentException {
143         MatrixUtils.checkSubtractionCompatible(this, m);
144 
145         final int dim = getRowDimension();
146         final double[] outData = new double[dim];
147         for (int i = 0; i < dim; i++) {
148             outData[i] = data[i] - m.data[i];
149         }
150 
151         return new DiagonalMatrix(outData, false);
152     }
153 
154     /**
155      * Returns the result of postmultiplying {@code this} by {@code m}.
156      *
157      * @param m matrix to postmultiply by
158      * @return {@code this * m}
159      * @throws MathIllegalArgumentException if
160      * {@code columnDimension(this) != rowDimension(m)}
161      */
162     public DiagonalMatrix multiply(final DiagonalMatrix m)
163         throws MathIllegalArgumentException {
164         MatrixUtils.checkMultiplicationCompatible(this, m);
165 
166         final int dim = getRowDimension();
167         final double[] outData = new double[dim];
168         for (int i = 0; i < dim; i++) {
169             outData[i] = data[i] * m.data[i];
170         }
171 
172         return new DiagonalMatrix(outData, false);
173     }
174 
175     /** {@inheritDoc} */
176     @Override
177     public RealMatrix multiply(final RealMatrix m)
178         throws MathIllegalArgumentException {
179         if (m instanceof DiagonalMatrix) {
180             return multiply((DiagonalMatrix) m);
181         } else {
182             MatrixUtils.checkMultiplicationCompatible(this, m);
183             final RealMatrix product = m.createMatrix(m.getRowDimension(), m.getColumnDimension());
184             product.walkInOptimizedOrder(new DefaultRealMatrixChangingVisitor() {
185                 /** {@inheritDoc} */
186                 @Override
187                 public double visit(int row, int column, double value) {
188                     return data[row] * m.getEntry(row, column);
189                 }
190             });
191             return product;
192         }
193     }
194 
195     /**
196      * Returns the result of postmultiplying {@code this} by {@code m^T}.
197      * @param m matrix to first transpose and second postmultiply by
198      * @return {@code this * m}
199      * @throws MathIllegalArgumentException if
200      * {@code columnDimension(this) != columnDimension(m)}
201      * @since 1.3
202      */
203     public DiagonalMatrix multiplyTransposed(final DiagonalMatrix m)
204         throws MathIllegalArgumentException {
205         // transposition is no-op for diagonal matrices
206         return multiply(m);
207     }
208 
209     /** {@inheritDoc} */
210     @Override
211     public RealMatrix multiplyTransposed(final RealMatrix m)
212         throws MathIllegalArgumentException {
213         if (m instanceof DiagonalMatrix) {
214             return multiplyTransposed((DiagonalMatrix) m);
215         } else {
216             MatrixUtils.checkSameColumnDimension(this, m);
217             final RealMatrix product = m.createMatrix(m.getColumnDimension(), m.getRowDimension());
218             product.walkInOptimizedOrder(new DefaultRealMatrixChangingVisitor() {
219                 /** {@inheritDoc} */
220                 @Override
221                 public double visit(int row, int column, double value) {
222                     return data[row] * m.getEntry(column, row);
223                 }
224             });
225             return product;
226         }
227     }
228 
229     /**
230      * Returns the result of postmultiplying {@code this^T} by {@code m}.
231      * @param m matrix to first transpose and second postmultiply by
232      * @return {@code this^T * m}
233      * @throws MathIllegalArgumentException if
234      * {@code columnDimension(this) != columnDimension(m)}
235      * @since 1.3
236      */
237     public DiagonalMatrix transposeMultiply(final DiagonalMatrix m)
238         throws MathIllegalArgumentException {
239         // transposition is no-op for diagonal matrices
240         return multiply(m);
241     }
242 
243     /** {@inheritDoc} */
244     @Override
245     public RealMatrix transposeMultiply(final RealMatrix m) {
246         if (m instanceof DiagonalMatrix) {
247             return transposeMultiply((DiagonalMatrix) m);
248         } else {
249             // transposition is no-op for diagonal matrices
250             return multiply(m);
251         }
252     }
253 
254     /** {@inheritDoc} */
255     @Override
256     public double[][] getData() {
257         final int dim = getRowDimension();
258         final double[][] out = new double[dim][dim];
259 
260         for (int i = 0; i < dim; i++) {
261             out[i][i] = data[i];
262         }
263 
264         return out;
265     }
266 
267     /**
268      * Gets a reference to the underlying data array.
269      *
270      * @return 1-dimensional array of entries.
271      */
272     public double[] getDataRef() {
273         return data; // NOPMD - returning an internal array is intentional and documented here
274     }
275 
276     /** {@inheritDoc} */
277     @Override
278     public double getEntry(final int row, final int column)
279         throws MathIllegalArgumentException {
280         MatrixUtils.checkMatrixIndex(this, row, column);
281         return row == column ? data[row] : 0;
282     }
283 
284     /** {@inheritDoc}
285      * @throws MathIllegalArgumentException if {@code row != column} and value is non-zero.
286      */
287     @Override
288     public void setEntry(final int row, final int column, final double value)
289         throws MathIllegalArgumentException {
290         if (row == column) {
291             MatrixUtils.checkRowIndex(this, row);
292             data[row] = value;
293         } else {
294             ensureZero(value);
295         }
296     }
297 
298     /** {@inheritDoc}
299      * @throws MathIllegalArgumentException if {@code row != column} and increment is non-zero.
300      */
301     @Override
302     public void addToEntry(final int row,
303                            final int column,
304                            final double increment)
305         throws MathIllegalArgumentException {
306         if (row == column) {
307             MatrixUtils.checkRowIndex(this, row);
308             data[row] += increment;
309         } else {
310             ensureZero(increment);
311         }
312     }
313 
314     /** {@inheritDoc} */
315     @Override
316     public void multiplyEntry(final int row,
317                               final int column,
318                               final double factor)
319         throws MathIllegalArgumentException {
320         // we don't care about non-diagonal elements for multiplication
321         if (row == column) {
322             MatrixUtils.checkRowIndex(this, row);
323             data[row] *= factor;
324         }
325     }
326 
327     /** {@inheritDoc} */
328     @Override
329     public int getRowDimension() {
330         return data.length;
331     }
332 
333     /** {@inheritDoc} */
334     @Override
335     public int getColumnDimension() {
336         return data.length;
337     }
338 
339     /** {@inheritDoc} */
340     @Override
341     public double[] operate(final double[] v)
342         throws MathIllegalArgumentException {
343         return multiply(new DiagonalMatrix(v, false)).getDataRef();
344     }
345 
346     /** {@inheritDoc} */
347     @Override
348     public double[] preMultiply(final double[] v)
349         throws MathIllegalArgumentException {
350         return operate(v);
351     }
352 
353     /** {@inheritDoc} */
354     @Override
355     public RealVector preMultiply(final RealVector v) throws MathIllegalArgumentException {
356         final double[] vectorData;
357         if (v instanceof ArrayRealVector) {
358             vectorData = ((ArrayRealVector) v).getDataRef();
359         } else {
360             vectorData = v.toArray();
361         }
362         return MatrixUtils.createRealVector(preMultiply(vectorData));
363     }
364 
365     /** Ensure a value is zero.
366      * @param value value to check
367      * @exception MathIllegalArgumentException if value is not zero
368      */
369     private void ensureZero(final double value) throws MathIllegalArgumentException {
370         if (!Precision.equals(0.0, value, 1)) {
371             throw new MathIllegalArgumentException(LocalizedCoreFormats.NUMBER_TOO_LARGE,
372                                                    FastMath.abs(value), 0);
373         }
374     }
375 
376     /**
377      * Computes the inverse of this diagonal matrix.
378      * <p>
379      * Note: this method will use a singularity threshold of 0,
380      * use {@link #inverse(double)} if a different threshold is needed.
381      *
382      * @return the inverse of {@code m}
383      * @throws MathIllegalArgumentException if the matrix is singular
384      */
385     public DiagonalMatrix inverse() throws MathIllegalArgumentException {
386         return inverse(0);
387     }
388 
389     /**
390      * Computes the inverse of this diagonal matrix.
391      *
392      * @param threshold Singularity threshold.
393      * @return the inverse of {@code m}
394      * @throws MathIllegalArgumentException if the matrix is singular
395      */
396     public DiagonalMatrix inverse(double threshold) throws MathIllegalArgumentException {
397         if (isSingular(threshold)) {
398             throw new MathIllegalArgumentException(LocalizedCoreFormats.SINGULAR_MATRIX);
399         }
400 
401         final double[] result = new double[data.length];
402         for (int i = 0; i < data.length; i++) {
403             result[i] = 1.0 / data[i];
404         }
405         return new DiagonalMatrix(result, false);
406     }
407 
408     /** Returns whether this diagonal matrix is singular, i.e. any diagonal entry
409      * is equal to {@code 0} within the given threshold.
410      *
411      * @param threshold Singularity threshold.
412      * @return {@code true} if the matrix is singular, {@code false} otherwise
413      */
414     public boolean isSingular(double threshold) {
415         for (int i = 0; i < data.length; i++) {
416             if (Precision.equals(data[i], 0.0, threshold)) {
417                 return true;
418             }
419         }
420         return false;
421     }
422 }