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 (double datum : data) {
416 if (Precision.equals(datum, 0.0, threshold)) {
417 return true;
418 }
419 }
420 return false;
421 }
422 }