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
9
* http://www.apache.org/licenses/LICENSE-2.0
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.
18
package org.apache.commons.math.linear;
22
* Class transforming any matrix to bi-diagonal shape.
23
* <p>Any m × n matrix A can be written as the product of three matrices:
24
* A = U × B × V<sup>T</sup> with U an m × m orthogonal matrix,
25
* B an m × n bi-diagonal matrix (lower diagonal if m < n, upper diagonal
26
* otherwise), and V an n × n orthogonal matrix.</p>
27
* <p>Transformation to bi-diagonal shape is often not a goal by itself, but it is
28
* an intermediate step in more general decomposition algorithms like {@link
29
* SingularValueDecomposition Singular Value Decomposition}. This class is therefore
30
* intended for internal use by the library and is not public. As a consequence of
31
* this explicitly limited scope, many methods directly returns references to
32
* internal arrays, not copies.</p>
33
* @version $Revision: 799857 $ $Date: 2009-08-01 09:07:12 -0400 (Sat, 01 Aug 2009) $
36
class BiDiagonalTransformer {
38
/** Householder vectors. */
39
private final double householderVectors[][];
42
private final double[] main;
44
/** Secondary diagonal. */
45
private final double[] secondary;
47
/** Cached value of U. */
48
private RealMatrix cachedU;
50
/** Cached value of B. */
51
private RealMatrix cachedB;
53
/** Cached value of V. */
54
private RealMatrix cachedV;
57
* Build the transformation to bi-diagonal shape of a matrix.
58
* @param matrix the matrix to transform.
60
public BiDiagonalTransformer(RealMatrix matrix) {
62
final int m = matrix.getRowDimension();
63
final int n = matrix.getColumnDimension();
64
final int p = Math.min(m, n);
65
householderVectors = matrix.getData();
67
secondary = new double[p - 1];
74
transformToUpperBiDiagonal();
76
transformToLowerBiDiagonal();
82
* Returns the matrix U of the transform.
83
* <p>U is an orthogonal matrix, i.e. its transpose is also its inverse.</p>
84
* @return the U matrix
86
public RealMatrix getU() {
88
if (cachedU == null) {
90
final int m = householderVectors.length;
91
final int n = householderVectors[0].length;
92
final int p = main.length;
93
final int diagOffset = (m >= n) ? 0 : 1;
94
final double[] diagonal = (m >= n) ? main : secondary;
95
cachedU = MatrixUtils.createRealMatrix(m, m);
97
// fill up the part of the matrix not affected by Householder transforms
98
for (int k = m - 1; k >= p; --k) {
99
cachedU.setEntry(k, k, 1);
102
// build up first part of the matrix by applying Householder transforms
103
for (int k = p - 1; k >= diagOffset; --k) {
104
final double[] hK = householderVectors[k];
105
cachedU.setEntry(k, k, 1);
106
if (hK[k - diagOffset] != 0.0) {
107
for (int j = k; j < m; ++j) {
109
for (int i = k; i < m; ++i) {
110
alpha -= cachedU.getEntry(i, j) * householderVectors[i][k - diagOffset];
112
alpha /= diagonal[k - diagOffset] * hK[k - diagOffset];
114
for (int i = k; i < m; ++i) {
115
cachedU.addToEntry(i, j, -alpha * householderVectors[i][k - diagOffset]);
120
if (diagOffset > 0) {
121
cachedU.setEntry(0, 0, 1);
126
// return the cached matrix
132
* Returns the bi-diagonal matrix B of the transform.
133
* @return the B matrix
135
public RealMatrix getB() {
137
if (cachedB == null) {
139
final int m = householderVectors.length;
140
final int n = householderVectors[0].length;
141
cachedB = MatrixUtils.createRealMatrix(m, n);
142
for (int i = 0; i < main.length; ++i) {
143
cachedB.setEntry(i, i, main[i]);
146
cachedB.setEntry(i, i - 1, secondary[i - 1]);
149
if (i < main.length - 1) {
150
cachedB.setEntry(i, i + 1, secondary[i]);
157
// return the cached matrix
163
* Returns the matrix V of the transform.
164
* <p>V is an orthogonal matrix, i.e. its transpose is also its inverse.</p>
165
* @return the V matrix
167
public RealMatrix getV() {
169
if (cachedV == null) {
171
final int m = householderVectors.length;
172
final int n = householderVectors[0].length;
173
final int p = main.length;
174
final int diagOffset = (m >= n) ? 1 : 0;
175
final double[] diagonal = (m >= n) ? secondary : main;
176
cachedV = MatrixUtils.createRealMatrix(n, n);
178
// fill up the part of the matrix not affected by Householder transforms
179
for (int k = n - 1; k >= p; --k) {
180
cachedV.setEntry(k, k, 1);
183
// build up first part of the matrix by applying Householder transforms
184
for (int k = p - 1; k >= diagOffset; --k) {
185
final double[] hK = householderVectors[k - diagOffset];
186
cachedV.setEntry(k, k, 1);
188
for (int j = k; j < n; ++j) {
190
for (int i = k; i < n; ++i) {
191
beta -= cachedV.getEntry(i, j) * hK[i];
193
beta /= diagonal[k - diagOffset] * hK[k];
195
for (int i = k; i < n; ++i) {
196
cachedV.addToEntry(i, j, -beta * hK[i]);
201
if (diagOffset > 0) {
202
cachedV.setEntry(0, 0, 1);
207
// return the cached matrix
213
* Get the Householder vectors of the transform.
214
* <p>Note that since this class is only intended for internal use,
215
* it returns directly a reference to its internal arrays, not a copy.</p>
216
* @return the main diagonal elements of the B matrix
218
double[][] getHouseholderVectorsRef() {
219
return householderVectors;
223
* Get the main diagonal elements of the matrix B of the transform.
224
* <p>Note that since this class is only intended for internal use,
225
* it returns directly a reference to its internal arrays, not a copy.</p>
226
* @return the main diagonal elements of the B matrix
228
double[] getMainDiagonalRef() {
233
* Get the secondary diagonal elements of the matrix B of the transform.
234
* <p>Note that since this class is only intended for internal use,
235
* it returns directly a reference to its internal arrays, not a copy.</p>
236
* @return the secondary diagonal elements of the B matrix
238
double[] getSecondaryDiagonalRef() {
243
* Check if the matrix is transformed to upper bi-diagonal.
244
* @return true if the matrix is transformed to upper bi-diagonal
246
boolean isUpperBiDiagonal() {
247
return householderVectors.length >= householderVectors[0].length;
251
* Transform original matrix to upper bi-diagonal form.
252
* <p>Transformation is done using alternate Householder transforms
253
* on columns and rows.</p>
255
private void transformToUpperBiDiagonal() {
257
final int m = householderVectors.length;
258
final int n = householderVectors[0].length;
259
for (int k = 0; k < n; k++) {
263
for (int i = k; i < m; ++i) {
264
final double c = householderVectors[i][k];
267
final double[] hK = householderVectors[k];
268
final double a = (hK[k] > 0) ? -Math.sqrt(xNormSqr) : Math.sqrt(xNormSqr);
272
for (int j = k + 1; j < n; ++j) {
274
for (int i = k; i < m; ++i) {
275
final double[] hI = householderVectors[i];
276
alpha -= hI[j] * hI[k];
278
alpha /= a * householderVectors[k][k];
279
for (int i = k; i < m; ++i) {
280
final double[] hI = householderVectors[i];
281
hI[j] -= alpha * hI[k];
289
for (int j = k + 1; j < n; ++j) {
290
final double c = hK[j];
293
final double b = (hK[k + 1] > 0) ? -Math.sqrt(xNormSqr) : Math.sqrt(xNormSqr);
297
for (int i = k + 1; i < m; ++i) {
298
final double[] hI = householderVectors[i];
300
for (int j = k + 1; j < n; ++j) {
301
beta -= hI[j] * hK[j];
303
beta /= b * hK[k + 1];
304
for (int j = k + 1; j < n; ++j) {
305
hI[j] -= beta * hK[j];
315
* Transform original matrix to lower bi-diagonal form.
316
* <p>Transformation is done using alternate Householder transforms
317
* on rows and columns.</p>
319
private void transformToLowerBiDiagonal() {
321
final int m = householderVectors.length;
322
final int n = householderVectors[0].length;
323
for (int k = 0; k < m; k++) {
326
final double[] hK = householderVectors[k];
328
for (int j = k; j < n; ++j) {
329
final double c = hK[j];
332
final double a = (hK[k] > 0) ? -Math.sqrt(xNormSqr) : Math.sqrt(xNormSqr);
336
for (int i = k + 1; i < m; ++i) {
337
final double[] hI = householderVectors[i];
339
for (int j = k; j < n; ++j) {
340
alpha -= hI[j] * hK[j];
342
alpha /= a * householderVectors[k][k];
343
for (int j = k; j < n; ++j) {
344
hI[j] -= alpha * hK[j];
351
final double[] hKp1 = householderVectors[k + 1];
353
for (int i = k + 1; i < m; ++i) {
354
final double c = householderVectors[i][k];
357
final double b = (hKp1[k] > 0) ? -Math.sqrt(xNormSqr) : Math.sqrt(xNormSqr);
361
for (int j = k + 1; j < n; ++j) {
363
for (int i = k + 1; i < m; ++i) {
364
final double[] hI = householderVectors[i];
365
beta -= hI[j] * hI[k];
368
for (int i = k + 1; i < m; ++i) {
369
final double[] hI = householderVectors[i];
370
hI[j] -= beta * hI[k];