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  
18  package org.apache.commons.math.linear;
19  
20  import junit.framework.Test;
21  import junit.framework.TestCase;
22  import junit.framework.TestSuite;
23  
24  public class QRDecompositionImplTest extends TestCase {
25      double[][] testData3x3NonSingular = { 
26              { 12, -51, 4 }, 
27              { 6, 167, -68 },
28              { -4, 24, -41 }, };
29  
30      double[][] testData3x3Singular = { 
31              { 1, 4, 7, }, 
32              { 2, 5, 8, },
33              { 3, 6, 9, }, };
34  
35      double[][] testData3x4 = { 
36              { 12, -51, 4, 1 }, 
37              { 6, 167, -68, 2 },
38              { -4, 24, -41, 3 }, };
39  
40      double[][] testData4x3 = { 
41              { 12, -51, 4, }, 
42              { 6, 167, -68, },
43              { -4, 24, -41, }, 
44              { -5, 34, 7, }, };
45  
46      private static final double entryTolerance = 10e-16;
47  
48      private static final double normTolerance = 10e-14;
49  
50      public QRDecompositionImplTest(String name) {
51          super(name);
52      }
53  
54      public static Test suite() {
55          TestSuite suite = new TestSuite(QRDecompositionImplTest.class);
56          suite.setName("QRDecompositionImpl Tests");
57          return suite;
58      }
59  
60      /** test dimensions */
61      public void testDimensions() {
62          RealMatrixImpl matrix = new RealMatrixImpl(testData3x3NonSingular);
63          QRDecomposition qr = new QRDecompositionImpl(matrix);
64          assertEquals("3x3 Q size", qr.getQ().getRowDimension(), 3);
65          assertEquals("3x3 Q size", qr.getQ().getColumnDimension(), 3);
66          assertEquals("3x3 R size", qr.getR().getRowDimension(), 3);
67          assertEquals("3x3 R size", qr.getR().getColumnDimension(), 3);
68  
69          matrix = new RealMatrixImpl(testData4x3);
70          qr = new QRDecompositionImpl(matrix);
71          assertEquals("4x3 Q size", qr.getQ().getRowDimension(), 4);
72          assertEquals("4x3 Q size", qr.getQ().getColumnDimension(), 4);
73          assertEquals("4x3 R size", qr.getR().getRowDimension(), 4);
74          assertEquals("4x3 R size", qr.getR().getColumnDimension(), 3);
75  
76          matrix = new RealMatrixImpl(testData3x4);
77          qr = new QRDecompositionImpl(matrix);
78          assertEquals("3x4 Q size", qr.getQ().getRowDimension(), 3);
79          assertEquals("3x4 Q size", qr.getQ().getColumnDimension(), 3);
80          assertEquals("3x4 R size", qr.getR().getRowDimension(), 3);
81          assertEquals("3x4 R size", qr.getR().getColumnDimension(), 4);
82      }
83  
84      /** test A = QR */
85      public void testAEqualQR() {
86          RealMatrix A = new RealMatrixImpl(testData3x3NonSingular);
87          QRDecomposition qr = new QRDecompositionImpl(A);
88          RealMatrix Q = qr.getQ();
89          RealMatrix R = qr.getR();
90          double norm = Q.multiply(R).subtract(A).getNorm();
91          assertEquals("3x3 nonsingular A = QR", 0, norm, normTolerance);
92  
93          RealMatrix matrix = new RealMatrixImpl(testData3x3Singular);
94          qr = new QRDecompositionImpl(matrix);
95          norm = qr.getQ().multiply(qr.getR()).subtract(matrix).getNorm();
96          assertEquals("3x3 singular A = QR", 0, norm, normTolerance);
97  
98          matrix = new RealMatrixImpl(testData3x4);
99          qr = new QRDecompositionImpl(matrix);
100         norm = qr.getQ().multiply(qr.getR()).subtract(matrix).getNorm();
101         assertEquals("3x4 A = QR", 0, norm, normTolerance);
102 
103         matrix = new RealMatrixImpl(testData4x3);
104         qr = new QRDecompositionImpl(matrix);
105         norm = qr.getQ().multiply(qr.getR()).subtract(matrix).getNorm();
106         assertEquals("4x3 A = QR", 0, norm, normTolerance);
107     }
108 
109     /** test the orthogonality of Q */
110     public void testQOrthogonal() {
111         RealMatrix matrix = new RealMatrixImpl(testData3x3NonSingular);
112         matrix = new QRDecompositionImpl(matrix).getQ();
113         RealMatrix eye = MatrixUtils.createRealIdentityMatrix(3);
114         double norm = matrix.transpose().multiply(matrix).subtract(eye)
115                 .getNorm();
116         assertEquals("3x3 nonsingular Q'Q = I", 0, norm, normTolerance);
117 
118         matrix = new RealMatrixImpl(testData3x3Singular);
119         matrix = new QRDecompositionImpl(matrix).getQ();
120         eye = MatrixUtils.createRealIdentityMatrix(3);
121         norm = matrix.transpose().multiply(matrix).subtract(eye)
122                 .getNorm();
123         assertEquals("3x3 singular Q'Q = I", 0, norm, normTolerance);
124 
125         matrix = new RealMatrixImpl(testData3x4);
126         matrix = new QRDecompositionImpl(matrix).getQ();
127         eye = MatrixUtils.createRealIdentityMatrix(3);
128         norm = matrix.transpose().multiply(matrix).subtract(eye)
129                 .getNorm();
130         assertEquals("3x4 Q'Q = I", 0, norm, normTolerance);
131 
132         matrix = new RealMatrixImpl(testData4x3);
133         matrix = new QRDecompositionImpl(matrix).getQ();
134         eye = MatrixUtils.createRealIdentityMatrix(4);
135         norm = matrix.transpose().multiply(matrix).subtract(eye)
136                 .getNorm();
137         assertEquals("4x3 Q'Q = I", 0, norm, normTolerance);
138     }
139 
140     /** test that R is upper triangular */
141     public void testRUpperTriangular() {
142         RealMatrixImpl matrix = new RealMatrixImpl(testData3x3NonSingular);
143         RealMatrix R = new QRDecompositionImpl(matrix).getR();
144         for (int i = 0; i < R.getRowDimension(); i++)
145             for (int j = 0; j < i; j++)
146                 assertEquals("R lower triangle", R.getEntry(i, j), 0,
147                         entryTolerance);
148 
149         matrix = new RealMatrixImpl(testData3x4);
150         R = new QRDecompositionImpl(matrix).getR();
151         for (int i = 0; i < R.getRowDimension(); i++)
152             for (int j = 0; j < i; j++)
153                 assertEquals("R lower triangle", R.getEntry(i, j), 0,
154                         entryTolerance);
155 
156         matrix = new RealMatrixImpl(testData4x3);
157         R = new QRDecompositionImpl(matrix).getR();
158         for (int i = 0; i < R.getRowDimension(); i++)
159             for (int j = 0; j < i; j++)
160                 assertEquals("R lower triangle", R.getEntry(i, j), 0,
161                         entryTolerance);
162     }
163 }