MatrixUtils.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.  *      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.math4.legacy.linear;

  18. import java.io.IOException;
  19. import java.io.ObjectInputStream;
  20. import java.io.ObjectOutputStream;
  21. import java.util.Arrays;

  22. import org.apache.commons.math4.legacy.core.Field;
  23. import org.apache.commons.math4.legacy.core.FieldElement;
  24. import org.apache.commons.math4.legacy.exception.DimensionMismatchException;
  25. import org.apache.commons.math4.legacy.exception.MathArithmeticException;
  26. import org.apache.commons.math4.legacy.exception.NoDataException;
  27. import org.apache.commons.math4.legacy.exception.NullArgumentException;
  28. import org.apache.commons.math4.legacy.exception.NumberIsTooSmallException;
  29. import org.apache.commons.math4.legacy.exception.OutOfRangeException;
  30. import org.apache.commons.math4.legacy.exception.ZeroException;
  31. import org.apache.commons.math4.legacy.exception.util.LocalizedFormats;
  32. import org.apache.commons.math4.core.jdkmath.JdkMath;
  33. import org.apache.commons.math4.legacy.core.MathArrays;
  34. import org.apache.commons.numbers.core.Precision;

  35. /**
  36.  * A collection of static methods that operate on or return matrices.
  37.  *
  38.  */
  39. public final class MatrixUtils {

  40.     /**
  41.      * The default format for {@link RealMatrix} objects.
  42.      * @since 3.1
  43.      */
  44.     public static final RealMatrixFormat DEFAULT_FORMAT = RealMatrixFormat.getInstance();

  45.     /**
  46.      * A format for {@link RealMatrix} objects compatible with octave.
  47.      * @since 3.1
  48.      */
  49.     public static final RealMatrixFormat OCTAVE_FORMAT = new RealMatrixFormat("[", "]", "", "", "; ", ", ");

  50.     /**
  51.      * Private constructor.
  52.      */
  53.     private MatrixUtils() {
  54.         super();
  55.     }

  56.     /**
  57.      * Returns a {@link RealMatrix} with specified dimensions.
  58.      * <p>The type of matrix returned depends on the dimension. Below
  59.      * 2<sup>12</sup> elements (i.e. 4096 elements or 64&times;64 for a
  60.      * square matrix) which can be stored in a 32kB array, a {@link
  61.      * Array2DRowRealMatrix} instance is built. Above this threshold a {@link
  62.      * BlockRealMatrix} instance is built.</p>
  63.      * <p>The matrix elements are all set to 0.0.</p>
  64.      * @param rows number of rows of the matrix
  65.      * @param columns number of columns of the matrix
  66.      * @return  RealMatrix with specified dimensions
  67.      * @see #createRealMatrix(double[][])
  68.      */
  69.     public static RealMatrix createRealMatrix(final int rows, final int columns) {
  70.         return (rows * columns <= 4096) ?
  71.                 new Array2DRowRealMatrix(rows, columns) : new BlockRealMatrix(rows, columns);
  72.     }

  73.     /**
  74.      * Returns a {@link FieldMatrix} with specified dimensions.
  75.      * <p>The type of matrix returned depends on the dimension. Below
  76.      * 2<sup>12</sup> elements (i.e. 4096 elements or 64&times;64 for a
  77.      * square matrix), a {@link FieldMatrix} instance is built. Above
  78.      * this threshold a {@link BlockFieldMatrix} instance is built.</p>
  79.      * <p>The matrix elements are all set to field.getZero().</p>
  80.      * @param <T> the type of the field elements
  81.      * @param field field to which the matrix elements belong
  82.      * @param rows number of rows of the matrix
  83.      * @param columns number of columns of the matrix
  84.      * @return  FieldMatrix with specified dimensions
  85.      * @see #createFieldMatrix(FieldElement[][])
  86.      * @since 2.0
  87.      */
  88.     public static <T extends FieldElement<T>> FieldMatrix<T> createFieldMatrix(final Field<T> field,
  89.                                                                                final int rows,
  90.                                                                                final int columns) {
  91.         return (rows * columns <= 4096) ?
  92.                 new Array2DRowFieldMatrix<>(field, rows, columns) : new BlockFieldMatrix<>(field, rows, columns);
  93.     }

  94.     /**
  95.      * Returns a {@link RealMatrix} whose entries are the values in the
  96.      * the input array.
  97.      * <p>The type of matrix returned depends on the dimension. Below
  98.      * 2<sup>12</sup> elements (i.e. 4096 elements or 64&times;64 for a
  99.      * square matrix) which can be stored in a 32kB array, a {@link
  100.      * Array2DRowRealMatrix} instance is built. Above this threshold a {@link
  101.      * BlockRealMatrix} instance is built.</p>
  102.      * <p>The input array is copied, not referenced.</p>
  103.      *
  104.      * @param data input array
  105.      * @return  RealMatrix containing the values of the array
  106.      * @throws org.apache.commons.math4.legacy.exception.DimensionMismatchException
  107.      * if {@code data} is not rectangular (not all rows have the same length).
  108.      * @throws NoDataException if a row or column is empty.
  109.      * @throws NullArgumentException if either {@code data} or {@code data[0]}
  110.      * is {@code null}.
  111.      * @throws DimensionMismatchException if {@code data} is not rectangular.
  112.      * @see #createRealMatrix(int, int)
  113.      */
  114.     public static RealMatrix createRealMatrix(double[][] data)
  115.         throws NullArgumentException, DimensionMismatchException,
  116.         NoDataException {
  117.         if (data == null ||
  118.             data[0] == null) {
  119.             throw new NullArgumentException();
  120.         }
  121.         return (data.length * data[0].length <= 4096) ?
  122.                 new Array2DRowRealMatrix(data) : new BlockRealMatrix(data);
  123.     }

  124.     /**
  125.      * Returns a {@link FieldMatrix} whose entries are the values in the
  126.      * the input array.
  127.      * <p>The type of matrix returned depends on the dimension. Below
  128.      * 2<sup>12</sup> elements (i.e. 4096 elements or 64&times;64 for a
  129.      * square matrix), a {@link FieldMatrix} instance is built. Above
  130.      * this threshold a {@link BlockFieldMatrix} instance is built.</p>
  131.      * <p>The input array is copied, not referenced.</p>
  132.      * @param <T> the type of the field elements
  133.      * @param data input array
  134.      * @return a matrix containing the values of the array.
  135.      * @throws org.apache.commons.math4.legacy.exception.DimensionMismatchException
  136.      * if {@code data} is not rectangular (not all rows have the same length).
  137.      * @throws NoDataException if a row or column is empty.
  138.      * @throws NullArgumentException if either {@code data} or {@code data[0]}
  139.      * is {@code null}.
  140.      * @see #createFieldMatrix(Field, int, int)
  141.      * @since 2.0
  142.      */
  143.     public static <T extends FieldElement<T>> FieldMatrix<T> createFieldMatrix(T[][] data)
  144.         throws DimensionMismatchException, NoDataException, NullArgumentException {
  145.         if (data == null ||
  146.             data[0] == null) {
  147.             throw new NullArgumentException();
  148.         }
  149.         return (data.length * data[0].length <= 4096) ?
  150.                 new Array2DRowFieldMatrix<>(data) : new BlockFieldMatrix<>(data);
  151.     }

  152.     /**
  153.      * Returns <code>dimension x dimension</code> identity matrix.
  154.      *
  155.      * @param dimension dimension of identity matrix to generate
  156.      * @return identity matrix
  157.      * @throws IllegalArgumentException if dimension is not positive
  158.      * @since 1.1
  159.      */
  160.     public static RealMatrix createRealIdentityMatrix(int dimension) {
  161.         final RealMatrix m = createRealMatrix(dimension, dimension);
  162.         for (int i = 0; i < dimension; ++i) {
  163.             m.setEntry(i, i, 1.0);
  164.         }
  165.         return m;
  166.     }

  167.     /**
  168.      * Returns <code>dimension x dimension</code> identity matrix.
  169.      *
  170.      * @param <T> the type of the field elements
  171.      * @param field field to which the elements belong
  172.      * @param dimension dimension of identity matrix to generate
  173.      * @return identity matrix
  174.      * @throws IllegalArgumentException if dimension is not positive
  175.      * @since 2.0
  176.      */
  177.     public static <T extends FieldElement<T>> FieldMatrix<T>
  178.         createFieldIdentityMatrix(final Field<T> field, final int dimension) {
  179.         final T zero = field.getZero();
  180.         final T one  = field.getOne();
  181.         final T[][] d = MathArrays.buildArray(field, dimension, dimension);
  182.         for (int row = 0; row < dimension; row++) {
  183.             final T[] dRow = d[row];
  184.             Arrays.fill(dRow, zero);
  185.             dRow[row] = one;
  186.         }
  187.         return new Array2DRowFieldMatrix<>(field, d, false);
  188.     }

  189.     /**
  190.      * Creates a diagonal matrix with the specified diagonal elements.
  191.      *
  192.      * @param diagonal Diagonal elements of the matrix.
  193.      * The array elements will be copied.
  194.      * @return a diagonal matrix instance.
  195.      *
  196.      * @see #createRealMatrixWithDiagonal(double[])
  197.      * @since 2.0
  198.      */
  199.     public static DiagonalMatrix createRealDiagonalMatrix(final double[] diagonal) {
  200.         return new DiagonalMatrix(diagonal, true);
  201.     }

  202.     /**
  203.      * Creates a dense matrix with the specified diagonal elements.
  204.      *
  205.      * @param diagonal Diagonal elements of the matrix.
  206.      * @return a matrix instance.
  207.      *
  208.      * @see #createRealDiagonalMatrix(double[])
  209.      * @since 4.0
  210.      */
  211.     public static RealMatrix createRealMatrixWithDiagonal(final double[] diagonal) {
  212.         final int size = diagonal.length;
  213.         final RealMatrix m = createRealMatrix(size, size);
  214.         for (int i = 0; i < size; i++) {
  215.             m.setEntry(i, i, diagonal[i]);
  216.         }
  217.         return m;
  218.     }

  219.     /**
  220.      * Returns a diagonal matrix with specified elements.
  221.      *
  222.      * @param <T> the type of the field elements
  223.      * @param diagonal diagonal elements of the matrix (the array elements
  224.      * will be copied)
  225.      * @return diagonal matrix
  226.      * @since 2.0
  227.      */
  228.     public static <T extends FieldElement<T>> FieldMatrix<T>
  229.         createFieldDiagonalMatrix(final T[] diagonal) {
  230.         final FieldMatrix<T> m =
  231.             createFieldMatrix(diagonal[0].getField(), diagonal.length, diagonal.length);
  232.         for (int i = 0; i < diagonal.length; ++i) {
  233.             m.setEntry(i, i, diagonal[i]);
  234.         }
  235.         return m;
  236.     }

  237.     /**
  238.      * Creates a {@link RealVector} using the data from the input array.
  239.      *
  240.      * @param data the input data
  241.      * @return a data.length RealVector
  242.      * @throws NoDataException if {@code data} is empty.
  243.      * @throws NullArgumentException if {@code data} is {@code null}.
  244.      */
  245.     public static RealVector createRealVector(double[] data)
  246.         throws NoDataException, NullArgumentException {
  247.         if (data == null) {
  248.             throw new NullArgumentException();
  249.         }
  250.         return new ArrayRealVector(data, true);
  251.     }

  252.     /**
  253.      * Creates a {@link FieldVector} using the data from the input array.
  254.      *
  255.      * @param <T> the type of the field elements
  256.      * @param data the input data
  257.      * @return a data.length FieldVector
  258.      * @throws NoDataException if {@code data} is empty.
  259.      * @throws NullArgumentException if {@code data} is {@code null}.
  260.      * @throws ZeroException if {@code data} has 0 elements
  261.      */
  262.     public static <T extends FieldElement<T>> FieldVector<T> createFieldVector(final T[] data)
  263.         throws NoDataException, NullArgumentException, ZeroException {
  264.         if (data == null) {
  265.             throw new NullArgumentException();
  266.         }
  267.         if (data.length == 0) {
  268.             throw new ZeroException(LocalizedFormats.VECTOR_MUST_HAVE_AT_LEAST_ONE_ELEMENT);
  269.         }
  270.         return new ArrayFieldVector<>(data[0].getField(), data, true);
  271.     }

  272.     /**
  273.      * Create a row {@link RealMatrix} using the data from the input
  274.      * array.
  275.      *
  276.      * @param rowData the input row data
  277.      * @return a 1 x rowData.length RealMatrix
  278.      * @throws NoDataException if {@code rowData} is empty.
  279.      * @throws NullArgumentException if {@code rowData} is {@code null}.
  280.      */
  281.     public static RealMatrix createRowRealMatrix(double[] rowData)
  282.         throws NoDataException, NullArgumentException {
  283.         if (rowData == null) {
  284.             throw new NullArgumentException();
  285.         }
  286.         final int nCols = rowData.length;
  287.         final RealMatrix m = createRealMatrix(1, nCols);
  288.         for (int i = 0; i < nCols; ++i) {
  289.             m.setEntry(0, i, rowData[i]);
  290.         }
  291.         return m;
  292.     }

  293.     /**
  294.      * Create a row {@link FieldMatrix} using the data from the input
  295.      * array.
  296.      *
  297.      * @param <T> the type of the field elements
  298.      * @param rowData the input row data
  299.      * @return a 1 x rowData.length FieldMatrix
  300.      * @throws NoDataException if {@code rowData} is empty.
  301.      * @throws NullArgumentException if {@code rowData} is {@code null}.
  302.      */
  303.     public static <T extends FieldElement<T>> FieldMatrix<T>
  304.         createRowFieldMatrix(final T[] rowData)
  305.         throws NoDataException, NullArgumentException {
  306.         if (rowData == null) {
  307.             throw new NullArgumentException();
  308.         }
  309.         final int nCols = rowData.length;
  310.         if (nCols == 0) {
  311.             throw new NoDataException(LocalizedFormats.AT_LEAST_ONE_COLUMN);
  312.         }
  313.         final FieldMatrix<T> m = createFieldMatrix(rowData[0].getField(), 1, nCols);
  314.         for (int i = 0; i < nCols; ++i) {
  315.             m.setEntry(0, i, rowData[i]);
  316.         }
  317.         return m;
  318.     }

  319.     /**
  320.      * Creates a column {@link RealMatrix} using the data from the input
  321.      * array.
  322.      *
  323.      * @param columnData  the input column data
  324.      * @return a columnData x 1 RealMatrix
  325.      * @throws NoDataException if {@code columnData} is empty.
  326.      * @throws NullArgumentException if {@code columnData} is {@code null}.
  327.      */
  328.     public static RealMatrix createColumnRealMatrix(double[] columnData)
  329.         throws NoDataException, NullArgumentException {
  330.         if (columnData == null) {
  331.             throw new NullArgumentException();
  332.         }
  333.         final int nRows = columnData.length;
  334.         final RealMatrix m = createRealMatrix(nRows, 1);
  335.         for (int i = 0; i < nRows; ++i) {
  336.             m.setEntry(i, 0, columnData[i]);
  337.         }
  338.         return m;
  339.     }

  340.     /**
  341.      * Creates a column {@link FieldMatrix} using the data from the input
  342.      * array.
  343.      *
  344.      * @param <T> the type of the field elements
  345.      * @param columnData  the input column data
  346.      * @return a columnData x 1 FieldMatrix
  347.      * @throws NoDataException if {@code data} is empty.
  348.      * @throws NullArgumentException if {@code columnData} is {@code null}.
  349.      */
  350.     public static <T extends FieldElement<T>> FieldMatrix<T>
  351.         createColumnFieldMatrix(final T[] columnData)
  352.         throws NoDataException, NullArgumentException {
  353.         if (columnData == null) {
  354.             throw new NullArgumentException();
  355.         }
  356.         final int nRows = columnData.length;
  357.         if (nRows == 0) {
  358.             throw new NoDataException(LocalizedFormats.AT_LEAST_ONE_ROW);
  359.         }
  360.         final FieldMatrix<T> m = createFieldMatrix(columnData[0].getField(), nRows, 1);
  361.         for (int i = 0; i < nRows; ++i) {
  362.             m.setEntry(i, 0, columnData[i]);
  363.         }
  364.         return m;
  365.     }

  366.     /**
  367.      * Checks whether a matrix is symmetric, within a given relative tolerance.
  368.      *
  369.      * @param matrix Matrix to check.
  370.      * @param relativeTolerance Tolerance of the symmetry check.
  371.      * @param raiseException If {@code true}, an exception will be raised if
  372.      * the matrix is not symmetric.
  373.      * @return {@code true} if {@code matrix} is symmetric.
  374.      * @throws NonSquareMatrixException if the matrix is not square.
  375.      * @throws NonSymmetricMatrixException if the matrix is not symmetric.
  376.      */
  377.     private static boolean isSymmetricInternal(RealMatrix matrix,
  378.                                                double relativeTolerance,
  379.                                                boolean raiseException) {
  380.         final int rows = matrix.getRowDimension();
  381.         if (rows != matrix.getColumnDimension()) {
  382.             if (raiseException) {
  383.                 throw new NonSquareMatrixException(rows, matrix.getColumnDimension());
  384.             } else {
  385.                 return false;
  386.             }
  387.         }
  388.         for (int i = 0; i < rows; i++) {
  389.             for (int j = i + 1; j < rows; j++) {
  390.                 final double mij = matrix.getEntry(i, j);
  391.                 final double mji = matrix.getEntry(j, i);
  392.                 if (JdkMath.abs(mij - mji) >
  393.                     JdkMath.max(JdkMath.abs(mij), JdkMath.abs(mji)) * relativeTolerance) {
  394.                     if (raiseException) {
  395.                         throw new NonSymmetricMatrixException(i, j, relativeTolerance);
  396.                     } else {
  397.                         return false;
  398.                     }
  399.                 }
  400.             }
  401.         }
  402.         return true;
  403.     }

  404.     /**
  405.      * Checks whether a matrix is symmetric.
  406.      *
  407.      * @param matrix Matrix to check.
  408.      * @param eps Relative tolerance.
  409.      * @throws NonSquareMatrixException if the matrix is not square.
  410.      * @throws NonSymmetricMatrixException if the matrix is not symmetric.
  411.      * @since 3.1
  412.      */
  413.     public static void checkSymmetric(RealMatrix matrix,
  414.                                       double eps) {
  415.         isSymmetricInternal(matrix, eps, true);
  416.     }

  417.     /**
  418.      * Checks whether a matrix is symmetric.
  419.      *
  420.      * @param matrix Matrix to check.
  421.      * @param eps Relative tolerance.
  422.      * @return {@code true} if {@code matrix} is symmetric.
  423.      * @since 3.1
  424.      */
  425.     public static boolean isSymmetric(RealMatrix matrix,
  426.                                       double eps) {
  427.         return isSymmetricInternal(matrix, eps, false);
  428.     }

  429.     /**
  430.      * Check if matrix indices are valid.
  431.      *
  432.      * @param m Matrix.
  433.      * @param row Row index to check.
  434.      * @param column Column index to check.
  435.      * @throws OutOfRangeException if {@code row} or {@code column} is not
  436.      * a valid index.
  437.      */
  438.     public static void checkMatrixIndex(final AnyMatrix m,
  439.                                         final int row, final int column)
  440.         throws OutOfRangeException {
  441.         checkRowIndex(m, row);
  442.         checkColumnIndex(m, column);
  443.     }

  444.     /**
  445.      * Check if a row index is valid.
  446.      *
  447.      * @param m Matrix.
  448.      * @param row Row index to check.
  449.      * @throws OutOfRangeException if {@code row} is not a valid index.
  450.      */
  451.     public static void checkRowIndex(final AnyMatrix m, final int row)
  452.         throws OutOfRangeException {
  453.         if (row < 0 ||
  454.             row >= m.getRowDimension()) {
  455.             throw new OutOfRangeException(LocalizedFormats.ROW_INDEX,
  456.                                           row, 0, m.getRowDimension() - 1);
  457.         }
  458.     }

  459.     /**
  460.      * Check if a column index is valid.
  461.      *
  462.      * @param m Matrix.
  463.      * @param column Column index to check.
  464.      * @throws OutOfRangeException if {@code column} is not a valid index.
  465.      */
  466.     public static void checkColumnIndex(final AnyMatrix m, final int column)
  467.         throws OutOfRangeException {
  468.         if (column < 0 || column >= m.getColumnDimension()) {
  469.             throw new OutOfRangeException(LocalizedFormats.COLUMN_INDEX,
  470.                                            column, 0, m.getColumnDimension() - 1);
  471.         }
  472.     }

  473.     /**
  474.      * Check if submatrix ranges indices are valid.
  475.      * Rows and columns are indicated counting from 0 to {@code n - 1}.
  476.      *
  477.      * @param m Matrix.
  478.      * @param startRow Initial row index.
  479.      * @param endRow Final row index.
  480.      * @param startColumn Initial column index.
  481.      * @param endColumn Final column index.
  482.      * @throws OutOfRangeException if the indices are invalid.
  483.      * @throws NumberIsTooSmallException if {@code endRow < startRow} or
  484.      * {@code endColumn < startColumn}.
  485.      */
  486.     public static void checkSubMatrixIndex(final AnyMatrix m,
  487.                                            final int startRow, final int endRow,
  488.                                            final int startColumn, final int endColumn)
  489.         throws NumberIsTooSmallException, OutOfRangeException {
  490.         checkRowIndex(m, startRow);
  491.         checkRowIndex(m, endRow);
  492.         if (endRow < startRow) {
  493.             throw new NumberIsTooSmallException(LocalizedFormats.INITIAL_ROW_AFTER_FINAL_ROW,
  494.                                                 endRow, startRow, false);
  495.         }

  496.         checkColumnIndex(m, startColumn);
  497.         checkColumnIndex(m, endColumn);
  498.         if (endColumn < startColumn) {
  499.             throw new NumberIsTooSmallException(LocalizedFormats.INITIAL_COLUMN_AFTER_FINAL_COLUMN,
  500.                                                 endColumn, startColumn, false);
  501.         }
  502.     }

  503.     /**
  504.      * Check if submatrix ranges indices are valid.
  505.      * Rows and columns are indicated counting from 0 to n-1.
  506.      *
  507.      * @param m Matrix.
  508.      * @param selectedRows Array of row indices.
  509.      * @param selectedColumns Array of column indices.
  510.      * @throws NullArgumentException if {@code selectedRows} or
  511.      * {@code selectedColumns} are {@code null}.
  512.      * @throws NoDataException if the row or column selections are empty (zero
  513.      * length).
  514.      * @throws OutOfRangeException if row or column selections are not valid.
  515.      */
  516.     public static void checkSubMatrixIndex(final AnyMatrix m,
  517.                                            final int[] selectedRows,
  518.                                            final int[] selectedColumns)
  519.         throws NoDataException, NullArgumentException, OutOfRangeException {
  520.         if (selectedRows == null) {
  521.             throw new NullArgumentException();
  522.         }
  523.         if (selectedColumns == null) {
  524.             throw new NullArgumentException();
  525.         }
  526.         if (selectedRows.length == 0) {
  527.             throw new NoDataException(LocalizedFormats.EMPTY_SELECTED_ROW_INDEX_ARRAY);
  528.         }
  529.         if (selectedColumns.length == 0) {
  530.             throw new NoDataException(LocalizedFormats.EMPTY_SELECTED_COLUMN_INDEX_ARRAY);
  531.         }

  532.         for (final int row : selectedRows) {
  533.             checkRowIndex(m, row);
  534.         }
  535.         for (final int column : selectedColumns) {
  536.             checkColumnIndex(m, column);
  537.         }
  538.     }

  539.     /**
  540.      * Check if matrices are addition compatible.
  541.      *
  542.      * @param left Left hand side matrix.
  543.      * @param right Right hand side matrix.
  544.      * @throws MatrixDimensionMismatchException if the matrices are not addition
  545.      * compatible.
  546.      */
  547.     public static void checkAdditionCompatible(final AnyMatrix left, final AnyMatrix right) {
  548.         left.checkAdd(right);
  549.     }

  550.     /**
  551.      * Check if matrices are subtraction compatible.
  552.      *
  553.      * @param left Left hand side matrix.
  554.      * @param right Right hand side matrix.
  555.      * @throws MatrixDimensionMismatchException if the matrices are not addition
  556.      * compatible.
  557.      */
  558.     public static void checkSubtractionCompatible(final AnyMatrix left, final AnyMatrix right) {
  559.         left.checkAdd(right);
  560.     }

  561.     /**
  562.      * Check if matrices are multiplication compatible.
  563.      *
  564.      * @param left Left hand side matrix.
  565.      * @param right Right hand side matrix.
  566.      * @throws DimensionMismatchException if matrices are not multiplication
  567.      * compatible.
  568.      */
  569.     public static void checkMultiplicationCompatible(final AnyMatrix left, final AnyMatrix right) {
  570.         left.checkMultiply(right);
  571.     }

  572.     /** Serialize a {@link RealVector}.
  573.      * <p>
  574.      * This method is intended to be called from within a private
  575.      * <code>writeObject</code> method (after a call to
  576.      * <code>oos.defaultWriteObject()</code>) in a class that has a
  577.      * {@link RealVector} field, which should be declared <code>transient</code>.
  578.      * This way, the default handling does not serialize the vector (the {@link
  579.      * RealVector} interface is not serializable by default) but this method does
  580.      * serialize it specifically.
  581.      * </p>
  582.      * <p>
  583.      * The following example shows how a simple class with a name and a real vector
  584.      * should be written:
  585.      * <pre><code>
  586.      * public class NamedVector implements Serializable {
  587.      *
  588.      *     private final String name;
  589.      *     private final transient RealVector coefficients;
  590.      *
  591.      *     // omitted constructors, getters ...
  592.      *
  593.      *     private void writeObject(ObjectOutputStream oos) throws IOException {
  594.      *         oos.defaultWriteObject();  // takes care of name field
  595.      *         MatrixUtils.serializeRealVector(coefficients, oos);
  596.      *     }
  597.      *
  598.      *     private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException {
  599.      *         ois.defaultReadObject();  // takes care of name field
  600.      *         MatrixUtils.deserializeRealVector(this, "coefficients", ois);
  601.      *     }
  602.      *
  603.      * }
  604.      * </code></pre>
  605.      *
  606.      * @param vector real vector to serialize
  607.      * @param oos stream where the real vector should be written
  608.      * @exception IOException if object cannot be written to stream
  609.      * @see #deserializeRealVector(Object, String, ObjectInputStream)
  610.      */
  611.     public static void serializeRealVector(final RealVector vector,
  612.                                            final ObjectOutputStream oos)
  613.         throws IOException {
  614.         final int n = vector.getDimension();
  615.         oos.writeInt(n);
  616.         for (int i = 0; i < n; ++i) {
  617.             oos.writeDouble(vector.getEntry(i));
  618.         }
  619.     }

  620.     /** Deserialize  a {@link RealVector} field in a class.
  621.      * <p>
  622.      * This method is intended to be called from within a private
  623.      * <code>readObject</code> method (after a call to
  624.      * <code>ois.defaultReadObject()</code>) in a class that has a
  625.      * {@link RealVector} field, which should be declared <code>transient</code>.
  626.      * This way, the default handling does not deserialize the vector (the {@link
  627.      * RealVector} interface is not serializable by default) but this method does
  628.      * deserialize it specifically.
  629.      * </p>
  630.      * @param instance instance in which the field must be set up
  631.      * @param fieldName name of the field within the class (may be private and final)
  632.      * @param ois stream from which the real vector should be read
  633.      * @exception ClassNotFoundException if a class in the stream cannot be found
  634.      * @exception IOException if object cannot be read from the stream
  635.      * @see #serializeRealVector(RealVector, ObjectOutputStream)
  636.      */
  637.     public static void deserializeRealVector(final Object instance,
  638.                                              final String fieldName,
  639.                                              final ObjectInputStream ois)
  640.       throws ClassNotFoundException, IOException {
  641.         try {

  642.             // read the vector data
  643.             final int n = ois.readInt();
  644.             final double[] data = new double[n];
  645.             for (int i = 0; i < n; ++i) {
  646.                 data[i] = ois.readDouble();
  647.             }

  648.             // create the instance
  649.             final RealVector vector = new ArrayRealVector(data, false);

  650.             // set up the field
  651.             final java.lang.reflect.Field f =
  652.                 instance.getClass().getDeclaredField(fieldName);
  653.             f.setAccessible(true);
  654.             f.set(instance, vector);
  655.         } catch (NoSuchFieldException nsfe) {
  656.             IOException ioe = new IOException();
  657.             ioe.initCause(nsfe);
  658.             throw ioe;
  659.         } catch (IllegalAccessException iae) {
  660.             IOException ioe = new IOException();
  661.             ioe.initCause(iae);
  662.             throw ioe;
  663.         }
  664.     }

  665.     /** Serialize a {@link RealMatrix}.
  666.      * <p>
  667.      * This method is intended to be called from within a private
  668.      * <code>writeObject</code> method (after a call to
  669.      * <code>oos.defaultWriteObject()</code>) in a class that has a
  670.      * {@link RealMatrix} field, which should be declared <code>transient</code>.
  671.      * This way, the default handling does not serialize the matrix (the {@link
  672.      * RealMatrix} interface is not serializable by default) but this method does
  673.      * serialize it specifically.
  674.      * </p>
  675.      * <p>
  676.      * The following example shows how a simple class with a name and a real matrix
  677.      * should be written:
  678.      * <pre><code>
  679.      * public class NamedMatrix implements Serializable {
  680.      *
  681.      *     private final String name;
  682.      *     private final transient RealMatrix coefficients;
  683.      *
  684.      *     // omitted constructors, getters ...
  685.      *
  686.      *     private void writeObject(ObjectOutputStream oos) throws IOException {
  687.      *         oos.defaultWriteObject();  // takes care of name field
  688.      *         MatrixUtils.serializeRealMatrix(coefficients, oos);
  689.      *     }
  690.      *
  691.      *     private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException {
  692.      *         ois.defaultReadObject();  // takes care of name field
  693.      *         MatrixUtils.deserializeRealMatrix(this, "coefficients", ois);
  694.      *     }
  695.      *
  696.      * }
  697.      * </code></pre>
  698.      *
  699.      * @param matrix real matrix to serialize
  700.      * @param oos stream where the real matrix should be written
  701.      * @exception IOException if object cannot be written to stream
  702.      * @see #deserializeRealMatrix(Object, String, ObjectInputStream)
  703.      */
  704.     public static void serializeRealMatrix(final RealMatrix matrix,
  705.                                            final ObjectOutputStream oos)
  706.         throws IOException {
  707.         final int n = matrix.getRowDimension();
  708.         final int m = matrix.getColumnDimension();
  709.         oos.writeInt(n);
  710.         oos.writeInt(m);
  711.         for (int i = 0; i < n; ++i) {
  712.             for (int j = 0; j < m; ++j) {
  713.                 oos.writeDouble(matrix.getEntry(i, j));
  714.             }
  715.         }
  716.     }

  717.     /** Deserialize  a {@link RealMatrix} field in a class.
  718.      * <p>
  719.      * This method is intended to be called from within a private
  720.      * <code>readObject</code> method (after a call to
  721.      * <code>ois.defaultReadObject()</code>) in a class that has a
  722.      * {@link RealMatrix} field, which should be declared <code>transient</code>.
  723.      * This way, the default handling does not deserialize the matrix (the {@link
  724.      * RealMatrix} interface is not serializable by default) but this method does
  725.      * deserialize it specifically.
  726.      * </p>
  727.      * @param instance instance in which the field must be set up
  728.      * @param fieldName name of the field within the class (may be private and final)
  729.      * @param ois stream from which the real matrix should be read
  730.      * @exception ClassNotFoundException if a class in the stream cannot be found
  731.      * @exception IOException if object cannot be read from the stream
  732.      * @see #serializeRealMatrix(RealMatrix, ObjectOutputStream)
  733.      */
  734.     public static void deserializeRealMatrix(final Object instance,
  735.                                              final String fieldName,
  736.                                              final ObjectInputStream ois)
  737.       throws ClassNotFoundException, IOException {
  738.         try {

  739.             // read the matrix data
  740.             final int n = ois.readInt();
  741.             final int m = ois.readInt();
  742.             final double[][] data = new double[n][m];
  743.             for (int i = 0; i < n; ++i) {
  744.                 final double[] dataI = data[i];
  745.                 for (int j = 0; j < m; ++j) {
  746.                     dataI[j] = ois.readDouble();
  747.                 }
  748.             }

  749.             // create the instance
  750.             final RealMatrix matrix = new Array2DRowRealMatrix(data, false);

  751.             // set up the field
  752.             final java.lang.reflect.Field f =
  753.                 instance.getClass().getDeclaredField(fieldName);
  754.             f.setAccessible(true);
  755.             f.set(instance, matrix);
  756.         } catch (NoSuchFieldException nsfe) {
  757.             IOException ioe = new IOException();
  758.             ioe.initCause(nsfe);
  759.             throw ioe;
  760.         } catch (IllegalAccessException iae) {
  761.             IOException ioe = new IOException();
  762.             ioe.initCause(iae);
  763.             throw ioe;
  764.         }
  765.     }

  766.     /**Solve  a  system of composed of a Lower Triangular Matrix
  767.      * {@link RealMatrix}.
  768.      * <p>
  769.      * This method is called to solve systems of equations which are
  770.      * of the lower triangular form. The matrix {@link RealMatrix}
  771.      * is assumed, though not checked, to be in lower triangular form.
  772.      * The vector {@link RealVector} is overwritten with the solution.
  773.      * The matrix is checked that it is square and its dimensions match
  774.      * the length of the vector.
  775.      * </p>
  776.      * @param rm RealMatrix which is lower triangular
  777.      * @param b  RealVector this is overwritten
  778.      * @throws DimensionMismatchException if the matrix and vector are not
  779.      * conformable
  780.      * @throws NonSquareMatrixException if the matrix {@code rm} is not square
  781.      * @throws MathArithmeticException if the absolute value of one of the diagonal
  782.      * coefficient of {@code rm} is lower than {@link Precision#SAFE_MIN}
  783.      */
  784.     public static void solveLowerTriangularSystem(RealMatrix rm, RealVector b)
  785.         throws DimensionMismatchException, MathArithmeticException,
  786.         NonSquareMatrixException {
  787.         if (rm == null || b == null || rm.getRowDimension() != b.getDimension()) {
  788.             throw new DimensionMismatchException(
  789.                     (rm == null) ? 0 : rm.getRowDimension(),
  790.                     (b == null) ? 0 : b.getDimension());
  791.         }
  792.         if( rm.getColumnDimension() != rm.getRowDimension() ){
  793.             throw new NonSquareMatrixException(rm.getRowDimension(),
  794.                                                rm.getColumnDimension());
  795.         }
  796.         int rows = rm.getRowDimension();
  797.         for( int i = 0 ; i < rows ; i++ ){
  798.             double diag = rm.getEntry(i, i);
  799.             if( JdkMath.abs(diag) < Precision.SAFE_MIN ){
  800.                 throw new MathArithmeticException(LocalizedFormats.ZERO_DENOMINATOR);
  801.             }
  802.             double bi = b.getEntry(i)/diag;
  803.             b.setEntry(i,  bi );
  804.             for( int j = i+1; j< rows; j++ ){
  805.                 b.setEntry(j, b.getEntry(j)-bi*rm.getEntry(j,i)  );
  806.             }
  807.         }
  808.     }

  809.     /** Solver a  system composed  of an Upper Triangular Matrix
  810.      * {@link RealMatrix}.
  811.      * <p>
  812.      * This method is called to solve systems of equations which are
  813.      * of the lower triangular form. The matrix {@link RealMatrix}
  814.      * is assumed, though not checked, to be in upper triangular form.
  815.      * The vector {@link RealVector} is overwritten with the solution.
  816.      * The matrix is checked that it is square and its dimensions match
  817.      * the length of the vector.
  818.      * </p>
  819.      * @param rm RealMatrix which is upper triangular
  820.      * @param b  RealVector this is overwritten
  821.      * @throws DimensionMismatchException if the matrix and vector are not
  822.      * conformable
  823.      * @throws NonSquareMatrixException if the matrix {@code rm} is not
  824.      * square
  825.      * @throws MathArithmeticException if the absolute value of one of the diagonal
  826.      * coefficient of {@code rm} is lower than {@link Precision#SAFE_MIN}
  827.      */
  828.     public static void solveUpperTriangularSystem(RealMatrix rm, RealVector b)
  829.         throws DimensionMismatchException, MathArithmeticException,
  830.         NonSquareMatrixException {
  831.         if (rm == null || b == null || rm.getRowDimension() != b.getDimension()) {
  832.             throw new DimensionMismatchException(
  833.                     (rm == null) ? 0 : rm.getRowDimension(),
  834.                     (b == null) ? 0 : b.getDimension());
  835.         }
  836.         if( rm.getColumnDimension() != rm.getRowDimension() ){
  837.             throw new NonSquareMatrixException(rm.getRowDimension(),
  838.                                                rm.getColumnDimension());
  839.         }
  840.         int rows = rm.getRowDimension();
  841.         for( int i = rows-1 ; i >-1 ; i-- ){
  842.             double diag = rm.getEntry(i, i);
  843.             if( JdkMath.abs(diag) < Precision.SAFE_MIN ){
  844.                 throw new MathArithmeticException(LocalizedFormats.ZERO_DENOMINATOR);
  845.             }
  846.             double bi = b.getEntry(i)/diag;
  847.             b.setEntry(i,  bi );
  848.             for( int j = i-1; j>-1; j-- ){
  849.                 b.setEntry(j, b.getEntry(j)-bi*rm.getEntry(j,i)  );
  850.             }
  851.         }
  852.     }

  853.     /**
  854.      * Computes the inverse of the given matrix by splitting it into
  855.      * 4 sub-matrices.
  856.      *
  857.      * @param m Matrix whose inverse must be computed.
  858.      * @param splitIndex Index that determines the "split" line and
  859.      * column.
  860.      * The element corresponding to this index will part of the
  861.      * upper-left sub-matrix.
  862.      * @return the inverse of {@code m}.
  863.      * @throws NonSquareMatrixException if {@code m} is not square.
  864.      */
  865.     public static RealMatrix blockInverse(RealMatrix m,
  866.                                           int splitIndex) {
  867.         final int n = m.getRowDimension();
  868.         if (m.getColumnDimension() != n) {
  869.             throw new NonSquareMatrixException(m.getRowDimension(),
  870.                                                m.getColumnDimension());
  871.         }

  872.         final int splitIndex1 = splitIndex + 1;

  873.         final RealMatrix a = m.getSubMatrix(0, splitIndex, 0, splitIndex);
  874.         final RealMatrix b = m.getSubMatrix(0, splitIndex, splitIndex1, n - 1);
  875.         final RealMatrix c = m.getSubMatrix(splitIndex1, n - 1, 0, splitIndex);
  876.         final RealMatrix d = m.getSubMatrix(splitIndex1, n - 1, splitIndex1, n - 1);

  877.         final SingularValueDecomposition aDec = new SingularValueDecomposition(a);
  878.         final DecompositionSolver aSolver = aDec.getSolver();
  879.         if (!aSolver.isNonSingular()) {
  880.             throw new SingularMatrixException();
  881.         }
  882.         final RealMatrix aInv = aSolver.getInverse();

  883.         final SingularValueDecomposition dDec = new SingularValueDecomposition(d);
  884.         final DecompositionSolver dSolver = dDec.getSolver();
  885.         if (!dSolver.isNonSingular()) {
  886.             throw new SingularMatrixException();
  887.         }
  888.         final RealMatrix dInv = dSolver.getInverse();

  889.         final RealMatrix tmp1 = a.subtract(b.multiply(dInv).multiply(c));
  890.         final SingularValueDecomposition tmp1Dec = new SingularValueDecomposition(tmp1);
  891.         final DecompositionSolver tmp1Solver = tmp1Dec.getSolver();
  892.         if (!tmp1Solver.isNonSingular()) {
  893.             throw new SingularMatrixException();
  894.         }
  895.         final RealMatrix result00 = tmp1Solver.getInverse();

  896.         final RealMatrix tmp2 = d.subtract(c.multiply(aInv).multiply(b));
  897.         final SingularValueDecomposition tmp2Dec = new SingularValueDecomposition(tmp2);
  898.         final DecompositionSolver tmp2Solver = tmp2Dec.getSolver();
  899.         if (!tmp2Solver.isNonSingular()) {
  900.             throw new SingularMatrixException();
  901.         }
  902.         final RealMatrix result11 = tmp2Solver.getInverse();

  903.         final RealMatrix result01 = aInv.multiply(b).multiply(result11).scalarMultiply(-1);
  904.         final RealMatrix result10 = dInv.multiply(c).multiply(result00).scalarMultiply(-1);

  905.         final RealMatrix result = new Array2DRowRealMatrix(n, n);
  906.         result.setSubMatrix(result00.getData(), 0, 0);
  907.         result.setSubMatrix(result01.getData(), 0, splitIndex1);
  908.         result.setSubMatrix(result10.getData(), splitIndex1, 0);
  909.         result.setSubMatrix(result11.getData(), splitIndex1, splitIndex1);

  910.         return result;
  911.     }

  912.     /**
  913.      * Computes the inverse of the given matrix.
  914.      * <p>
  915.      * By default, the inverse of the matrix is computed using the QR-decomposition,
  916.      * unless a more efficient method can be determined for the input matrix.
  917.      * <p>
  918.      * Note: this method will use a singularity threshold of 0,
  919.      * use {@link #inverse(RealMatrix, double)} if a different threshold is needed.
  920.      *
  921.      * @param matrix Matrix whose inverse shall be computed
  922.      * @return the inverse of {@code matrix}
  923.      * @throws NullArgumentException if {@code matrix} is {@code null}
  924.      * @throws SingularMatrixException if m is singular
  925.      * @throws NonSquareMatrixException if matrix is not square
  926.      * @since 3.3
  927.      */
  928.     public static RealMatrix inverse(RealMatrix matrix)
  929.             throws NullArgumentException, SingularMatrixException, NonSquareMatrixException {
  930.         return inverse(matrix, 0);
  931.     }

  932.     /**
  933.      * Computes the inverse of the given matrix.
  934.      * <p>
  935.      * By default, the inverse of the matrix is computed using the QR-decomposition,
  936.      * unless a more efficient method can be determined for the input matrix.
  937.      *
  938.      * @param matrix Matrix whose inverse shall be computed
  939.      * @param threshold Singularity threshold
  940.      * @return the inverse of {@code m}
  941.      * @throws NullArgumentException if {@code matrix} is {@code null}
  942.      * @throws SingularMatrixException if matrix is singular
  943.      * @throws NonSquareMatrixException if matrix is not square
  944.      * @since 3.3
  945.      */
  946.     public static RealMatrix inverse(RealMatrix matrix, double threshold)
  947.             throws NullArgumentException, SingularMatrixException, NonSquareMatrixException {

  948.         NullArgumentException.check(matrix);

  949.         if (!matrix.isSquare()) {
  950.             throw new NonSquareMatrixException(matrix.getRowDimension(),
  951.                                                matrix.getColumnDimension());
  952.         }

  953.         if (matrix instanceof DiagonalMatrix) {
  954.             return ((DiagonalMatrix) matrix).inverse(threshold);
  955.         } else {
  956.             QRDecomposition decomposition = new QRDecomposition(matrix, threshold);
  957.             return decomposition.getSolver().getInverse();
  958.         }
  959.     }
  960. }