BiDiagonalTransformer.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 org.hipparchus.util.FastMath;


  23. /**
  24.  * Class transforming any matrix to bi-diagonal shape.
  25.  * <p>Any m &times; n matrix A can be written as the product of three matrices:
  26.  * A = U &times; B &times; V<sup>T</sup> with U an m &times; m orthogonal matrix,
  27.  * B an m &times; n bi-diagonal matrix (lower diagonal if m &lt; n, upper diagonal
  28.  * otherwise), and V an n &times; n orthogonal matrix.</p>
  29.  * <p>Transformation to bi-diagonal shape is often not a goal by itself, but it is
  30.  * an intermediate step in more general decomposition algorithms like {@link
  31.  * SingularValueDecomposition Singular Value Decomposition}. This class is therefore
  32.  * intended for internal use by the library and is not public. As a consequence of
  33.  * this explicitly limited scope, many methods directly returns references to
  34.  * internal arrays, not copies.</p>
  35.  */
  36. class BiDiagonalTransformer {

  37.     /** Householder vectors. */
  38.     private final double[][] householderVectors;

  39.     /** Main diagonal. */
  40.     private final double[] main;

  41.     /** Secondary diagonal. */
  42.     private final double[] secondary;

  43.     /** Cached value of U. */
  44.     private RealMatrix cachedU;

  45.     /** Cached value of B. */
  46.     private RealMatrix cachedB;

  47.     /** Cached value of V. */
  48.     private RealMatrix cachedV;

  49.     /**
  50.      * Build the transformation to bi-diagonal shape of a matrix.
  51.      * @param matrix the matrix to transform.
  52.      */
  53.     BiDiagonalTransformer(RealMatrix matrix) {

  54.         final int m = matrix.getRowDimension();
  55.         final int n = matrix.getColumnDimension();
  56.         final int p = FastMath.min(m, n);
  57.         householderVectors = matrix.getData();
  58.         main      = new double[p];
  59.         secondary = new double[p - 1];
  60.         cachedU   = null;
  61.         cachedB   = null;
  62.         cachedV   = null;

  63.         // transform matrix
  64.         if (m >= n) {
  65.             transformToUpperBiDiagonal();
  66.         } else {
  67.             transformToLowerBiDiagonal();
  68.         }

  69.     }

  70.     /**
  71.      * Returns the matrix U of the transform.
  72.      * <p>U is an orthogonal matrix, i.e. its transpose is also its inverse.</p>
  73.      * @return the U matrix
  74.      */
  75.     public RealMatrix getU() {

  76.         if (cachedU == null) {

  77.             final int m = householderVectors.length;
  78.             final int n = householderVectors[0].length;
  79.             final int p = main.length;
  80.             final int diagOffset    = (m >= n) ? 0 : 1;
  81.             final double[] diagonal = (m >= n) ? main : secondary;
  82.             double[][] ua = new double[m][m];

  83.             // fill up the part of the matrix not affected by Householder transforms
  84.             for (int k = m - 1; k >= p; --k) {
  85.                 ua[k][k] = 1;
  86.             }

  87.             // build up first part of the matrix by applying Householder transforms
  88.             for (int k = p - 1; k >= diagOffset; --k) {
  89.                 final double[] hK = householderVectors[k];
  90.                 ua[k][k] = 1;
  91.                 if (hK[k - diagOffset] != 0.0) {
  92.                     for (int j = k; j < m; ++j) {
  93.                         double alpha = 0;
  94.                         for (int i = k; i < m; ++i) {
  95.                             alpha -= ua[i][j] * householderVectors[i][k - diagOffset];
  96.                         }
  97.                         alpha /= diagonal[k - diagOffset] * hK[k - diagOffset];

  98.                         for (int i = k; i < m; ++i) {
  99.                             ua[i][j] += -alpha * householderVectors[i][k - diagOffset];
  100.                         }
  101.                     }
  102.                 }
  103.             }
  104.             if (diagOffset > 0) {
  105.                 ua[0][0] = 1;
  106.             }
  107.             cachedU = MatrixUtils.createRealMatrix(ua);
  108.         }

  109.         // return the cached matrix
  110.         return cachedU;

  111.     }

  112.     /**
  113.      * Returns the bi-diagonal matrix B of the transform.
  114.      * @return the B matrix
  115.      */
  116.     public RealMatrix getB() {

  117.         if (cachedB == null) {

  118.             final int m = householderVectors.length;
  119.             final int n = householderVectors[0].length;
  120.             double[][] ba = new double[m][n];
  121.             for (int i = 0; i < main.length; ++i) {
  122.                 ba[i][i] = main[i];
  123.                 if (m < n) {
  124.                     if (i > 0) {
  125.                         ba[i][i-1] = secondary[i - 1];
  126.                     }
  127.                 } else {
  128.                     if (i < main.length - 1) {
  129.                         ba[i][i+1] = secondary[i];
  130.                     }
  131.                 }
  132.             }
  133.             cachedB = MatrixUtils.createRealMatrix(ba);
  134.         }

  135.         // return the cached matrix
  136.         return cachedB;

  137.     }

  138.     /**
  139.      * Returns the matrix V of the transform.
  140.      * <p>V is an orthogonal matrix, i.e. its transpose is also its inverse.</p>
  141.      * @return the V matrix
  142.      */
  143.     public RealMatrix getV() {

  144.         if (cachedV == null) {

  145.             final int m = householderVectors.length;
  146.             final int n = householderVectors[0].length;
  147.             final int p = main.length;
  148.             final int diagOffset    = (m >= n) ? 1 : 0;
  149.             final double[] diagonal = (m >= n) ? secondary : main;
  150.             double[][] va = new double[n][n];

  151.             // fill up the part of the matrix not affected by Householder transforms
  152.             for (int k = n - 1; k >= p; --k) {
  153.                 va[k][k] = 1;
  154.             }

  155.             // build up first part of the matrix by applying Householder transforms
  156.             for (int k = p - 1; k >= diagOffset; --k) {
  157.                 final double[] hK = householderVectors[k - diagOffset];
  158.                 va[k][k] = 1;
  159.                 if (hK[k] != 0.0) {
  160.                     for (int j = k; j < n; ++j) {
  161.                         double beta = 0;
  162.                         for (int i = k; i < n; ++i) {
  163.                             beta -= va[i][j] * hK[i];
  164.                         }
  165.                         beta /= diagonal[k - diagOffset] * hK[k];

  166.                         for (int i = k; i < n; ++i) {
  167.                             va[i][j] += -beta * hK[i];
  168.                         }
  169.                     }
  170.                 }
  171.             }
  172.             if (diagOffset > 0) {
  173.                 va[0][0] = 1;
  174.             }
  175.             cachedV = MatrixUtils.createRealMatrix(va);
  176.         }

  177.         // return the cached matrix
  178.         return cachedV;

  179.     }

  180.     /**
  181.      * Get the Householder vectors of the transform.
  182.      * <p>Note that since this class is only intended for internal use,
  183.      * it returns directly a reference to its internal arrays, not a copy.</p>
  184.      * @return the main diagonal elements of the B matrix
  185.      */
  186.     double[][] getHouseholderVectorsRef() {
  187.         return householderVectors; // NOPMD - returning an internal array is intentional and documented here
  188.     }

  189.     /**
  190.      * Get the main diagonal elements of the matrix B of the transform.
  191.      * <p>Note that since this class is only intended for internal use,
  192.      * it returns directly a reference to its internal arrays, not a copy.</p>
  193.      * @return the main diagonal elements of the B matrix
  194.      */
  195.     double[] getMainDiagonalRef() {
  196.         return main; // NOPMD - returning an internal array is intentional and documented here
  197.     }

  198.     /**
  199.      * Get the secondary diagonal elements of the matrix B of the transform.
  200.      * <p>Note that since this class is only intended for internal use,
  201.      * it returns directly a reference to its internal arrays, not a copy.</p>
  202.      * @return the secondary diagonal elements of the B matrix
  203.      */
  204.     double[] getSecondaryDiagonalRef() {
  205.         return secondary; // NOPMD - returning an internal array is intentional and documented here
  206.     }

  207.     /**
  208.      * Check if the matrix is transformed to upper bi-diagonal.
  209.      * @return true if the matrix is transformed to upper bi-diagonal
  210.      */
  211.     boolean isUpperBiDiagonal() {
  212.         return householderVectors.length >=  householderVectors[0].length;
  213.     }

  214.     /**
  215.      * Transform original matrix to upper bi-diagonal form.
  216.      * <p>Transformation is done using alternate Householder transforms
  217.      * on columns and rows.</p>
  218.      */
  219.     private void transformToUpperBiDiagonal() {

  220.         final int m = householderVectors.length;
  221.         final int n = householderVectors[0].length;
  222.         for (int k = 0; k < n; k++) {

  223.             //zero-out a column
  224.             double xNormSqr = 0;
  225.             for (int i = k; i < m; ++i) {
  226.                 final double c = householderVectors[i][k];
  227.                 xNormSqr += c * c;
  228.             }
  229.             final double[] hK = householderVectors[k];
  230.             final double a = (hK[k] > 0) ? -FastMath.sqrt(xNormSqr) : FastMath.sqrt(xNormSqr);
  231.             main[k] = a;
  232.             if (a != 0.0) {
  233.                 hK[k] -= a;
  234.                 for (int j = k + 1; j < n; ++j) {
  235.                     double alpha = 0;
  236.                     for (int i = k; i < m; ++i) {
  237.                         final double[] hI = householderVectors[i];
  238.                         alpha -= hI[j] * hI[k];
  239.                     }
  240.                     alpha /= a * householderVectors[k][k];
  241.                     for (int i = k; i < m; ++i) {
  242.                         final double[] hI = householderVectors[i];
  243.                         hI[j] -= alpha * hI[k];
  244.                     }
  245.                 }
  246.             }

  247.             if (k < n - 1) {
  248.                 //zero-out a row
  249.                 xNormSqr = 0;
  250.                 for (int j = k + 1; j < n; ++j) {
  251.                     final double c = hK[j];
  252.                     xNormSqr += c * c;
  253.                 }
  254.                 final double b = (hK[k + 1] > 0) ? -FastMath.sqrt(xNormSqr) : FastMath.sqrt(xNormSqr);
  255.                 secondary[k] = b;
  256.                 if (b != 0.0) {
  257.                     hK[k + 1] -= b;
  258.                     for (int i = k + 1; i < m; ++i) {
  259.                         final double[] hI = householderVectors[i];
  260.                         double beta = 0;
  261.                         for (int j = k + 1; j < n; ++j) {
  262.                             beta -= hI[j] * hK[j];
  263.                         }
  264.                         beta /= b * hK[k + 1];
  265.                         for (int j = k + 1; j < n; ++j) {
  266.                             hI[j] -= beta * hK[j];
  267.                         }
  268.                     }
  269.                 }
  270.             }

  271.         }
  272.     }

  273.     /**
  274.      * Transform original matrix to lower bi-diagonal form.
  275.      * <p>Transformation is done using alternate Householder transforms
  276.      * on rows and columns.</p>
  277.      */
  278.     private void transformToLowerBiDiagonal() {

  279.         final int m = householderVectors.length;
  280.         final int n = householderVectors[0].length;
  281.         for (int k = 0; k < m; k++) {

  282.             //zero-out a row
  283.             final double[] hK = householderVectors[k];
  284.             double xNormSqr = 0;
  285.             for (int j = k; j < n; ++j) {
  286.                 final double c = hK[j];
  287.                 xNormSqr += c * c;
  288.             }
  289.             final double a = (hK[k] > 0) ? -FastMath.sqrt(xNormSqr) : FastMath.sqrt(xNormSqr);
  290.             main[k] = a;
  291.             if (a != 0.0) {
  292.                 hK[k] -= a;
  293.                 for (int i = k + 1; i < m; ++i) {
  294.                     final double[] hI = householderVectors[i];
  295.                     double alpha = 0;
  296.                     for (int j = k; j < n; ++j) {
  297.                         alpha -= hI[j] * hK[j];
  298.                     }
  299.                     alpha /= a * householderVectors[k][k];
  300.                     for (int j = k; j < n; ++j) {
  301.                         hI[j] -= alpha * hK[j];
  302.                     }
  303.                 }
  304.             }

  305.             if (k < m - 1) {
  306.                 //zero-out a column
  307.                 final double[] hKp1 = householderVectors[k + 1];
  308.                 xNormSqr = 0;
  309.                 for (int i = k + 1; i < m; ++i) {
  310.                     final double c = householderVectors[i][k];
  311.                     xNormSqr += c * c;
  312.                 }
  313.                 final double b = (hKp1[k] > 0) ? -FastMath.sqrt(xNormSqr) : FastMath.sqrt(xNormSqr);
  314.                 secondary[k] = b;
  315.                 if (b != 0.0) {
  316.                     hKp1[k] -= b;
  317.                     for (int j = k + 1; j < n; ++j) {
  318.                         double beta = 0;
  319.                         for (int i = k + 1; i < m; ++i) {
  320.                             final double[] hI = householderVectors[i];
  321.                             beta -= hI[j] * hI[k];
  322.                         }
  323.                         beta /= b * hKp1[k];
  324.                         for (int i = k + 1; i < m; ++i) {
  325.                             final double[] hI = householderVectors[i];
  326.                             hI[j] -= beta * hI[k];
  327.                         }
  328.                     }
  329.                 }
  330.             }

  331.         }
  332.     }

  333. }