1 /*
2 * Copyright (C) 2012 Alberto Irurueta Carro (alberto@irurueta.com)
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16 package com.irurueta.algebra;
17
18 /**
19 * This decomposer computes RQ decomposition, which consists on factoring
20 * provided input matrix into an upper triangular matrix (R) and an orthogonal
21 * matrix (Q). In other words, if input matrix is A, then A = R * Q
22 */
23 @SuppressWarnings("DuplicatedCode")
24 public class RQDecomposer extends Decomposer {
25
26 /**
27 * Internal QR decomposer used behind the scenes to compute RQ
28 * decomposition. Notice that QR and RQ decompositions are related and for
29 * that reason QRDecomposer is used
30 */
31 private final QRDecomposer internalDecomposer;
32
33 /**
34 * Constructor of this class.
35 */
36 public RQDecomposer() {
37 super();
38 internalDecomposer = new QRDecomposer();
39 }
40
41 /**
42 * Constructor of this class.
43 *
44 * @param inputMatrix Reference to input matrix to be decomposed.
45 */
46 public RQDecomposer(final Matrix inputMatrix) {
47 super(inputMatrix);
48 internalDecomposer = new QRDecomposer();
49 }
50
51 /**
52 * Returns decomposer type corresponding to RQ decomposition.
53 *
54 * @return Decomposer type.
55 */
56 @Override
57 public DecomposerType getDecomposerType() {
58 return DecomposerType.RQ_DECOMPOSITION;
59 }
60
61 /**
62 * Sets reference to input matrix to be decomposed.
63 *
64 * @param inputMatrix Reference to input matrix to be decomposed.
65 * @throws LockedException Exception thrown if attempting to call this
66 * method while this instance remains locked.
67 */
68 @Override
69 public void setInputMatrix(final Matrix inputMatrix) throws LockedException {
70 super.setInputMatrix(inputMatrix);
71 internalDecomposer.setInputMatrix(inputMatrix);
72 }
73
74
75 /**
76 * Returns boolean indicating whether decomposition has been computed and
77 * results can be retrieved.
78 * Attempting to retrieve decomposition results when not available, will
79 * probably raise a NotAvailableException.
80 *
81 * @return Boolean indicating whether decomposition has been computed and
82 * results can be retrieved.
83 */
84 @Override
85 public boolean isDecompositionAvailable() {
86 return internalDecomposer.isDecompositionAvailable();
87 }
88
89 /**
90 * This method computes RQ matrix decomposition, which consists on factoring
91 * provided input matrix into an upper triangular matrix (R) and an
92 * orthogonal matrix (Q).
93 * In other words, if input matrix is A, then: A = R * Q
94 * Note: During execution of this method, this instance will be locked, and
95 * hence attempting to set some parameters might raise a LockedException.
96 * Note: After execution of this method, RQ decomposition will be available
97 * and operations such as retrieving R and Q matrices will be able to be
98 * done. Attempting to call any of such operations before calling this
99 * method wil raise a NotAvailableException because they require computation
100 * of QR decomposition first.
101 *
102 * @throws NotReadyException Exception thrown if attempting to call this
103 * method when this instance is not ready (i.e. no input matrix has been
104 * provided).
105 * @throws LockedException Exception thrown if this decomposer is already
106 * locked before calling this method. Notice that this method will actually
107 * lock this instance while it is being executed.
108 * @throws DecomposerException Exception thrown if for any reason
109 * decomposition fails while being executed, like when provided input matrix
110 * has fewer rows than columns.
111 */
112 @Override
113 public void decompose() throws NotReadyException, LockedException, DecomposerException {
114
115 if (!isReady()) {
116 throw new NotReadyException();
117 }
118 if (isLocked()) {
119 throw new LockedException();
120 }
121
122 Matrix tmp = null;
123
124 locked = true;
125
126 final var rows = inputMatrix.getRows();
127 final var columns = inputMatrix.getColumns();
128
129 try {
130 tmp = new Matrix(columns, rows);
131
132 for (var j = 0; j < columns; j++) {
133 for (var i = 0; i < rows; i++) {
134 tmp.setElementAt(j, rows - i - 1,
135 inputMatrix.getElementAt(i, j));
136 }
137 }
138 } catch (final WrongSizeException ignore) {
139 // never happens
140 }
141
142 internalDecomposer.setInputMatrix(tmp);
143 internalDecomposer.decompose();
144
145 locked = false;
146 }
147
148 /**
149 * Returns upper triangular factor matrix.
150 * RQ decomposition decomposes input matrix into R (upper triangular
151 * matrix), and Q, which is an orthogonal matrix.
152 *
153 * @return Upper triangular factor matrix.
154 * @throws NotAvailableException Exception thrown if attempting to call this
155 * method before computing RQ decomposition. To avoid this exception call
156 * decompose() method first.
157 * @see #decompose()
158 */
159 public Matrix getR() throws NotAvailableException {
160
161 if (!isDecompositionAvailable()) {
162 throw new NotAvailableException();
163 }
164
165 final var rows = inputMatrix.getRows();
166 final var columns = inputMatrix.getColumns();
167
168 final var r2 = internalDecomposer.getR();
169
170 // Left-right flipped identity
171 // Instance initialized to zero
172 Matrix flipI = null;
173 try {
174 flipI = new Matrix(rows, rows);
175
176 flipI.initialize(0.0);
177
178 for (var j = 0; j < rows; j++) {
179 for (var i = 0; i < rows; i++) {
180 if (i == rows - j - 1) flipI.setElementAt(i, j, 1.0);
181 }
182 }
183 } catch (final WrongSizeException ignore) {
184 //never happens
185 }
186
187 // Big permutation
188 Matrix perm = null;
189 if (flipI != null) {
190 try {
191 perm = Matrix.identity(columns, columns);
192
193 // Copy flipped identity into top-left corner
194 perm.setSubmatrix(0, 0, rows - 1, rows - 1,
195 flipI);
196
197 perm.multiply(r2); //perm * r2
198 perm.multiply(flipI); //perm * r2 * flipI
199 perm.transpose();
200
201 } catch (final WrongSizeException ignore) {
202 // never happens
203 }
204 }
205 return perm;
206 }
207
208 /**
209 * Returns the economy-sized orthogonal factor matrix.
210 * RQ decomposition decomposes input matrix into R, which is an upper
211 * triangular matrix and Q (orthogonal matrix)
212 *
213 * @return Orthogonal factor matrix.
214 * @throws NotAvailableException Exception thrown if attempting to call this
215 * method before computing RQ decomposition. To avoid this exception call
216 * decompose() method first.
217 * @see #decompose()
218 */
219 public Matrix getQ() throws NotAvailableException {
220 if (!isDecompositionAvailable()) {
221 throw new NotAvailableException();
222 }
223
224 final var rows = inputMatrix.getRows();
225 final var columns = inputMatrix.getColumns();
226
227 final var q2 = internalDecomposer.getQ();
228
229 // Left-right flipped identity
230 // Instance initialized to zero
231 Matrix flipI = null;
232 try {
233 flipI = new Matrix(rows, rows);
234
235 flipI.initialize(0.0);
236
237 for (int j = 0; j < rows; j++) {
238 for (int i = 0; i < rows; i++) {
239 if (i == rows - j - 1) {
240 flipI.setElementAt(i, j, 1.0);
241 }
242 }
243 }
244 } catch (final WrongSizeException ignore) {
245 //never happens
246 }
247
248
249 // Big permutation
250 Matrix perm = null;
251 try {
252 perm = Matrix.identity(columns, columns);
253
254 // Copy flipped identity into top-left corner
255 perm.setSubmatrix(0, 0, rows - 1, rows - 1, flipI);
256
257 } catch (final WrongSizeException ignore) {
258 // never happens
259 }
260
261
262 Matrix q = null;
263 if (perm != null) {
264 try {
265 q = q2.multiplyAndReturnNew(perm); // qTrans = q2 * perm
266 q.transpose();
267 } catch (final WrongSizeException ignore) {
268 //never happens
269 }
270 }
271
272 return q;
273 }
274 }