View Javadoc
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 }