DiagonalMatrix.java

  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.  * This is not the original file distributed by the Apache Software Foundation
  19.  * It has been modified by the Hipparchus project
  20.  */
  21. package org.hipparchus.linear;

  22. import java.io.Serializable;

  23. import org.hipparchus.exception.LocalizedCoreFormats;
  24. import org.hipparchus.exception.MathIllegalArgumentException;
  25. import org.hipparchus.exception.NullArgumentException;
  26. import org.hipparchus.util.FastMath;
  27. import org.hipparchus.util.MathUtils;
  28. import org.hipparchus.util.Precision;

  29. /**
  30.  * Implementation of a diagonal matrix.
  31.  *
  32.  */
  33. public class DiagonalMatrix extends AbstractRealMatrix
  34.     implements Serializable {
  35.     /** Serializable version identifier. */
  36.     private static final long serialVersionUID = 20121229L;
  37.     /** Entries of the diagonal. */
  38.     private final double[] data;

  39.     /**
  40.      * Creates a matrix with the supplied dimension.
  41.      *
  42.      * @param dimension Number of rows and columns in the new matrix.
  43.      * @throws MathIllegalArgumentException if the dimension is
  44.      * not positive.
  45.      */
  46.     public DiagonalMatrix(final int dimension)
  47.         throws MathIllegalArgumentException {
  48.         super(dimension, dimension);
  49.         data = new double[dimension];
  50.     }

  51.     /**
  52.      * Creates a matrix using the input array as the underlying data.
  53.      * <br>
  54.      * The input array is copied, not referenced.
  55.      *
  56.      * @param d Data for the new matrix.
  57.      */
  58.     public DiagonalMatrix(final double[] d) {
  59.         this(d, true);
  60.     }

  61.     /**
  62.      * Creates a matrix using the input array as the underlying data.
  63.      * <br>
  64.      * If an array is created specially in order to be embedded in a
  65.      * this instance and not used directly, the {@code copyArray} may be
  66.      * set to {@code false}.
  67.      * This will prevent the copying and improve performance as no new
  68.      * array will be built and no data will be copied.
  69.      *
  70.      * @param d Data for new matrix.
  71.      * @param copyArray if {@code true}, the input array will be copied,
  72.      * otherwise it will be referenced.
  73.      * @exception NullArgumentException if d is null
  74.      */
  75.     public DiagonalMatrix(final double[] d, final boolean copyArray)
  76.         throws NullArgumentException {
  77.         MathUtils.checkNotNull(d);
  78.         data = copyArray ? d.clone() : d;
  79.     }

  80.     /**
  81.      * {@inheritDoc}
  82.      *
  83.      * @throws MathIllegalArgumentException if the requested dimensions are not equal.
  84.      */
  85.     @Override
  86.     public RealMatrix createMatrix(final int rowDimension,
  87.                                    final int columnDimension)
  88.         throws MathIllegalArgumentException {
  89.         if (rowDimension != columnDimension) {
  90.             throw new MathIllegalArgumentException(LocalizedCoreFormats.DIMENSIONS_MISMATCH,
  91.                                                    rowDimension, columnDimension);
  92.         }

  93.         return new DiagonalMatrix(rowDimension);
  94.     }

  95.     /** {@inheritDoc} */
  96.     @Override
  97.     public RealMatrix copy() {
  98.         return new DiagonalMatrix(data);
  99.     }

  100.     /**
  101.      * Compute the sum of {@code this} and {@code m}.
  102.      *
  103.      * @param m Matrix to be added.
  104.      * @return {@code this + m}.
  105.      * @throws MathIllegalArgumentException if {@code m} is not the same
  106.      * size as {@code this}.
  107.      */
  108.     public DiagonalMatrix add(final DiagonalMatrix m)
  109.         throws MathIllegalArgumentException {
  110.         // Safety check.
  111.         MatrixUtils.checkAdditionCompatible(this, m);

  112.         final int dim = getRowDimension();
  113.         final double[] outData = new double[dim];
  114.         for (int i = 0; i < dim; i++) {
  115.             outData[i] = data[i] + m.data[i];
  116.         }

  117.         return new DiagonalMatrix(outData, false);
  118.     }

  119.     /**
  120.      * Returns {@code this} minus {@code m}.
  121.      *
  122.      * @param m Matrix to be subtracted.
  123.      * @return {@code this - m}
  124.      * @throws MathIllegalArgumentException if {@code m} is not the same
  125.      * size as {@code this}.
  126.      */
  127.     public DiagonalMatrix subtract(final DiagonalMatrix m)
  128.         throws MathIllegalArgumentException {
  129.         MatrixUtils.checkSubtractionCompatible(this, m);

  130.         final int dim = getRowDimension();
  131.         final double[] outData = new double[dim];
  132.         for (int i = 0; i < dim; i++) {
  133.             outData[i] = data[i] - m.data[i];
  134.         }

  135.         return new DiagonalMatrix(outData, false);
  136.     }

  137.     /**
  138.      * Returns the result of postmultiplying {@code this} by {@code m}.
  139.      *
  140.      * @param m matrix to postmultiply by
  141.      * @return {@code this * m}
  142.      * @throws MathIllegalArgumentException if
  143.      * {@code columnDimension(this) != rowDimension(m)}
  144.      */
  145.     public DiagonalMatrix multiply(final DiagonalMatrix m)
  146.         throws MathIllegalArgumentException {
  147.         MatrixUtils.checkMultiplicationCompatible(this, m);

  148.         final int dim = getRowDimension();
  149.         final double[] outData = new double[dim];
  150.         for (int i = 0; i < dim; i++) {
  151.             outData[i] = data[i] * m.data[i];
  152.         }

  153.         return new DiagonalMatrix(outData, false);
  154.     }

  155.     /** {@inheritDoc} */
  156.     @Override
  157.     public RealMatrix multiply(final RealMatrix m)
  158.         throws MathIllegalArgumentException {
  159.         if (m instanceof DiagonalMatrix) {
  160.             return multiply((DiagonalMatrix) m);
  161.         } else {
  162.             MatrixUtils.checkMultiplicationCompatible(this, m);
  163.             final RealMatrix product = m.createMatrix(m.getRowDimension(), m.getColumnDimension());
  164.             product.walkInOptimizedOrder(new DefaultRealMatrixChangingVisitor() {
  165.                 /** {@inheritDoc} */
  166.                 @Override
  167.                 public double visit(int row, int column, double value) {
  168.                     return data[row] * m.getEntry(row, column);
  169.                 }
  170.             });
  171.             return product;
  172.         }
  173.     }

  174.     /**
  175.      * Returns the result of postmultiplying {@code this} by {@code m^T}.
  176.      * @param m matrix to first transpose and second postmultiply by
  177.      * @return {@code this * m}
  178.      * @throws MathIllegalArgumentException if
  179.      * {@code columnDimension(this) != columnDimension(m)}
  180.      * @since 1.3
  181.      */
  182.     public DiagonalMatrix multiplyTransposed(final DiagonalMatrix m)
  183.         throws MathIllegalArgumentException {
  184.         // transposition is no-op for diagonal matrices
  185.         return multiply(m);
  186.     }

  187.     /** {@inheritDoc} */
  188.     @Override
  189.     public RealMatrix multiplyTransposed(final RealMatrix m)
  190.         throws MathIllegalArgumentException {
  191.         if (m instanceof DiagonalMatrix) {
  192.             return multiplyTransposed((DiagonalMatrix) m);
  193.         } else {
  194.             MatrixUtils.checkSameColumnDimension(this, m);
  195.             final RealMatrix product = m.createMatrix(m.getColumnDimension(), m.getRowDimension());
  196.             product.walkInOptimizedOrder(new DefaultRealMatrixChangingVisitor() {
  197.                 /** {@inheritDoc} */
  198.                 @Override
  199.                 public double visit(int row, int column, double value) {
  200.                     return data[row] * m.getEntry(column, row);
  201.                 }
  202.             });
  203.             return product;
  204.         }
  205.     }

  206.     /**
  207.      * Returns the result of postmultiplying {@code this^T} by {@code m}.
  208.      * @param m matrix to first transpose and second postmultiply by
  209.      * @return {@code this^T * m}
  210.      * @throws MathIllegalArgumentException if
  211.      * {@code columnDimension(this) != columnDimension(m)}
  212.      * @since 1.3
  213.      */
  214.     public DiagonalMatrix transposeMultiply(final DiagonalMatrix m)
  215.         throws MathIllegalArgumentException {
  216.         // transposition is no-op for diagonal matrices
  217.         return multiply(m);
  218.     }

  219.     /** {@inheritDoc} */
  220.     @Override
  221.     public RealMatrix transposeMultiply(final RealMatrix m) {
  222.         if (m instanceof DiagonalMatrix) {
  223.             return transposeMultiply((DiagonalMatrix) m);
  224.         } else {
  225.             // transposition is no-op for diagonal matrices
  226.             return multiply(m);
  227.         }
  228.     }

  229.     /** {@inheritDoc} */
  230.     @Override
  231.     public double[][] getData() {
  232.         final int dim = getRowDimension();
  233.         final double[][] out = new double[dim][dim];

  234.         for (int i = 0; i < dim; i++) {
  235.             out[i][i] = data[i];
  236.         }

  237.         return out;
  238.     }

  239.     /**
  240.      * Gets a reference to the underlying data array.
  241.      *
  242.      * @return 1-dimensional array of entries.
  243.      */
  244.     public double[] getDataRef() {
  245.         return data; // NOPMD - returning an internal array is intentional and documented here
  246.     }

  247.     /** {@inheritDoc} */
  248.     @Override
  249.     public double getEntry(final int row, final int column)
  250.         throws MathIllegalArgumentException {
  251.         MatrixUtils.checkMatrixIndex(this, row, column);
  252.         return row == column ? data[row] : 0;
  253.     }

  254.     /** {@inheritDoc}
  255.      * @throws MathIllegalArgumentException if {@code row != column} and value is non-zero.
  256.      */
  257.     @Override
  258.     public void setEntry(final int row, final int column, final double value)
  259.         throws MathIllegalArgumentException {
  260.         if (row == column) {
  261.             MatrixUtils.checkRowIndex(this, row);
  262.             data[row] = value;
  263.         } else {
  264.             ensureZero(value);
  265.         }
  266.     }

  267.     /** {@inheritDoc}
  268.      * @throws MathIllegalArgumentException if {@code row != column} and increment is non-zero.
  269.      */
  270.     @Override
  271.     public void addToEntry(final int row,
  272.                            final int column,
  273.                            final double increment)
  274.         throws MathIllegalArgumentException {
  275.         if (row == column) {
  276.             MatrixUtils.checkRowIndex(this, row);
  277.             data[row] += increment;
  278.         } else {
  279.             ensureZero(increment);
  280.         }
  281.     }

  282.     /** {@inheritDoc} */
  283.     @Override
  284.     public void multiplyEntry(final int row,
  285.                               final int column,
  286.                               final double factor)
  287.         throws MathIllegalArgumentException {
  288.         // we don't care about non-diagonal elements for multiplication
  289.         if (row == column) {
  290.             MatrixUtils.checkRowIndex(this, row);
  291.             data[row] *= factor;
  292.         }
  293.     }

  294.     /** {@inheritDoc} */
  295.     @Override
  296.     public int getRowDimension() {
  297.         return data.length;
  298.     }

  299.     /** {@inheritDoc} */
  300.     @Override
  301.     public int getColumnDimension() {
  302.         return data.length;
  303.     }

  304.     /** {@inheritDoc} */
  305.     @Override
  306.     public double[] operate(final double[] v)
  307.         throws MathIllegalArgumentException {
  308.         return multiply(new DiagonalMatrix(v, false)).getDataRef();
  309.     }

  310.     /** {@inheritDoc} */
  311.     @Override
  312.     public double[] preMultiply(final double[] v)
  313.         throws MathIllegalArgumentException {
  314.         return operate(v);
  315.     }

  316.     /** {@inheritDoc} */
  317.     @Override
  318.     public RealVector preMultiply(final RealVector v) throws MathIllegalArgumentException {
  319.         final double[] vectorData;
  320.         if (v instanceof ArrayRealVector) {
  321.             vectorData = ((ArrayRealVector) v).getDataRef();
  322.         } else {
  323.             vectorData = v.toArray();
  324.         }
  325.         return MatrixUtils.createRealVector(preMultiply(vectorData));
  326.     }

  327.     /** Ensure a value is zero.
  328.      * @param value value to check
  329.      * @exception MathIllegalArgumentException if value is not zero
  330.      */
  331.     private void ensureZero(final double value) throws MathIllegalArgumentException {
  332.         if (!Precision.equals(0.0, value, 1)) {
  333.             throw new MathIllegalArgumentException(LocalizedCoreFormats.NUMBER_TOO_LARGE,
  334.                                                    FastMath.abs(value), 0);
  335.         }
  336.     }

  337.     /**
  338.      * Computes the inverse of this diagonal matrix.
  339.      * <p>
  340.      * Note: this method will use a singularity threshold of 0,
  341.      * use {@link #inverse(double)} if a different threshold is needed.
  342.      *
  343.      * @return the inverse of {@code m}
  344.      * @throws MathIllegalArgumentException if the matrix is singular
  345.      */
  346.     public DiagonalMatrix inverse() throws MathIllegalArgumentException {
  347.         return inverse(0);
  348.     }

  349.     /**
  350.      * Computes the inverse of this diagonal matrix.
  351.      *
  352.      * @param threshold Singularity threshold.
  353.      * @return the inverse of {@code m}
  354.      * @throws MathIllegalArgumentException if the matrix is singular
  355.      */
  356.     public DiagonalMatrix inverse(double threshold) throws MathIllegalArgumentException {
  357.         if (isSingular(threshold)) {
  358.             throw new MathIllegalArgumentException(LocalizedCoreFormats.SINGULAR_MATRIX);
  359.         }

  360.         final double[] result = new double[data.length];
  361.         for (int i = 0; i < data.length; i++) {
  362.             result[i] = 1.0 / data[i];
  363.         }
  364.         return new DiagonalMatrix(result, false);
  365.     }

  366.     /** Returns whether this diagonal matrix is singular, i.e. any diagonal entry
  367.      * is equal to {@code 0} within the given threshold.
  368.      *
  369.      * @param threshold Singularity threshold.
  370.      * @return {@code true} if the matrix is singular, {@code false} otherwise
  371.      */
  372.     public boolean isSingular(double threshold) {
  373.         for (double datum : data) {
  374.             if (Precision.equals(datum, 0.0, threshold)) {
  375.                 return true;
  376.             }
  377.         }
  378.         return false;
  379.     }
  380. }