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 economy QR decomposition, which is faster than
20 * typical QR decomposition.
21 */
22 @SuppressWarnings("DuplicatedCode")
23 public class EconomyQRDecomposer extends Decomposer {
24
25 /**
26 * Constant defining default round error when determining full rank of
27 * matrices. This value is zero by default
28 */
29 public static final double DEFAULT_ROUND_ERROR = 0.0;
30
31 /**
32 * Constant defining minimum allowed round error value when determining full
33 * rank of matrices.
34 */
35 public static final double MIN_ROUND_ERROR = 0.0;
36
37 /**
38 * Internal matrix containing results of decomposition.
39 */
40 private Matrix qr;
41
42 /**
43 * Internal array containing diagonal of R.
44 */
45 private double[] rDiag;
46
47 /**
48 * Constructor of this class.
49 */
50 public EconomyQRDecomposer() {
51 super();
52 qr = null;
53 rDiag = null;
54 }
55
56 /**
57 * Constructor of this class.
58 *
59 * @param inputMatrix Reference to input matrix to be decomposed.
60 */
61 public EconomyQRDecomposer(final Matrix inputMatrix) {
62 super(inputMatrix);
63 qr = null;
64 rDiag = null;
65 }
66
67 /**
68 * Returns decomposer type corresponding to Economy QR decomposition.
69 *
70 * @return Decomposer type.
71 */
72 @Override
73 public DecomposerType getDecomposerType() {
74 return DecomposerType.QR_ECONOMY_DECOMPOSITION;
75 }
76
77 /**
78 * Sets reference to input matrix to be decomposed.
79 *
80 * @param inputMatrix Reference to input matrix to be decomposed.
81 * @throws LockedException Exception thrown if attempting to call this
82 * method while this instance remains locked.
83 */
84 @Override
85 public void setInputMatrix(final Matrix inputMatrix) throws LockedException {
86 super.setInputMatrix(inputMatrix);
87 qr = null;
88 rDiag = null;
89 }
90
91 /**
92 * Returns boolean indicating whether decomposition has been computed and
93 * results can be retrieved.
94 * Attempting to retrieve decomposition results when not available, will
95 * probably raise a NotAvailableException.
96 *
97 * @return Boolean indicating whether decomposition has been computed and
98 * results can be retrieved.
99 */
100 @Override
101 public boolean isDecompositionAvailable() {
102 return qr != null;
103 }
104
105 /**
106 * This method computes QR matrix decomposition, which consists on factoring
107 * provided input matrix into an orthogonal matrix (Q) and an upper
108 * triangular matrix (R).
109 * In other words, if input matrix is A, then: A = Q * R
110 * Note: During execution of this method, this instance will be locked.
111 * Note: After execution of this method, QR decomposition will be available
112 * and operations such as retrieving Q and R matrices or solving systems of
113 * linear equations will be able to be done. Attempting to call any of such
114 * operations before calling this method will raise a NotAvailableException
115 * because they require computation of QR decomposition first.
116 *
117 * @throws NotReadyException Exception thrown if attempting to call this
118 * method when this instance is not ready (i.e. no input matrix has been
119 * provided).
120 * @throws LockedException Exception thrown if this decomposer is already
121 * locked before calling this method. Notice that this method will actually
122 * lock this instance while it is being executed.
123 */
124 @Override
125 public void decompose() throws NotReadyException, LockedException {
126
127 if (!isReady()) {
128 throw new NotReadyException();
129 }
130 if (isLocked()) {
131 throw new LockedException();
132 }
133
134 locked = true;
135
136 final var rows = inputMatrix.getRows();
137 final var columns = inputMatrix.getColumns();
138 qr = new Matrix(inputMatrix);
139
140 rDiag = new double[columns];
141 double nrm;
142 double s;
143
144 // Main loop
145 for (var k = 0; k < columns; k++) {
146 // Compute 2-norm of k-th column without under/overflow
147 nrm = 0.0;
148
149 for (var i = k; i < rows; i++) {
150 nrm = Math.sqrt(nrm * nrm + Math.pow(qr.getElementAt(i, k), 2.0));
151 }
152
153 if (nrm != 0.0) {
154 // Form k-th Householder vector
155 if (qr.getElementAt(k, k) < 0.0) {
156 nrm = -nrm;
157 }
158
159 for (var i = k; i < rows; i++) {
160 qr.setElementAt(i, k, qr.getElementAt(i, k) / nrm);
161 }
162 qr.setElementAt(k, k, qr.getElementAt(k, k) + 1.0);
163
164 // Apply transformation to remaining columns
165 for (var j = k + 1; j < columns; j++) {
166 s = 0.0;
167 for (var i = k; i < rows; i++) {
168 s += qr.getElementAt(i, k) * qr.getElementAt(i, j);
169 }
170 s = -s / qr.getElementAt(k, k);
171 for (var i = k; i < rows; i++) {
172 qr.setElementAt(i, j, qr.getElementAt(i, j) + s * qr.getElementAt(i, k));
173 }
174 }
175 }
176
177 rDiag[k] = -nrm;
178 }
179
180 locked = false;
181 }
182
183 /**
184 * Returns boolean indicating whether provided input matrix has full rank or
185 * not.
186 * Squared matrices having full rank also have determinant different from
187 * zero.
188 * Note: Because of rounding errors, testing whether a matrix has full rank
189 * or not, might obtain unreliable results especially for non-square
190 * matrices. In such cases matrices usually tend to be considered as full
191 * rank even when they are not.
192 *
193 * @return Boolean indicating whether provided input matrix has full rank or
194 * not.
195 * @throws NotAvailableException Exception thrown if attempting to call this
196 * method before computing QR decomposition. To avoid this exception call
197 * decompose() method first.
198 * @throws WrongSizeException Exception thrown if provided rounding error is
199 * lower than minimum allowed value (MIN_ROUND_ERROR).
200 * @see #decompose()
201 */
202 public boolean isFullRank() throws NotAvailableException, WrongSizeException {
203 return isFullRank(DEFAULT_ROUND_ERROR);
204 }
205
206 /**
207 * Returns boolean indicating whether provided input matrix has full rank or
208 * not.
209 * Squared matrices having full rank also have determinant different from
210 * zero.
211 * Note: Because of rounding errors, testing whether a matrix has full rank
212 * or not, might obtain unreliable results especially for non-square
213 * matrices. In such cases matrices usually tend to be considered as full
214 * rank even when they are not.
215 *
216 * @param roundingError Determines the amount of margin given to determine
217 * whether a matrix has full rank or not due to rounding errors. If not
218 * provided, by default rounding error is set to zero, but this value can
219 * be relaxed if needed.
220 * @return Boolean indicating whether provided input matrix has full rank or
221 * not.
222 * @throws NotAvailableException Exception thrown if attempting to call this
223 * method before computing QR decomposition. To avoid this exception call
224 * decompose() method first.
225 * @throws WrongSizeException Exception thrown if provided input matrix has
226 * less rows than columns.
227 * @throws IllegalArgumentException Exception thrown if provided rounding
228 * error is lower than minimum allowed value (MIN_ROUND_ERROR).
229 * @see #decompose()
230 */
231 public boolean isFullRank(final double roundingError) throws NotAvailableException, WrongSizeException {
232
233 if (!isDecompositionAvailable()) {
234 throw new NotAvailableException();
235 }
236 if (roundingError < MIN_ROUND_ERROR) {
237 throw new IllegalArgumentException();
238 }
239
240 final var rows = inputMatrix.getRows();
241 final var columns = inputMatrix.getColumns();
242
243 if (rows < columns) {
244 throw new WrongSizeException();
245 }
246
247 for (var j = 0; j < columns; j++) {
248 if (Math.abs(rDiag[j]) < roundingError) {
249 return false;
250 }
251 }
252 return true;
253 }
254
255 /**
256 * Computes the Householder vectors and store them in provided matrix.
257 * Provided matrix will be resized if needed
258 *
259 * @param h Matrix where Householder vectors will be stored
260 * @throws NotAvailableException Exception thrown if attempting to call this
261 * method before computing QR decomposition. To avoid this exception call
262 * decompose() method first.
263 * @see #decompose()
264 */
265 public void getH(final Matrix h) throws NotAvailableException {
266 if (!isDecompositionAvailable()) {
267 throw new NotAvailableException();
268 }
269
270 final var rows = inputMatrix.getRows();
271 final var columns = inputMatrix.getColumns();
272 if (h.getRows() != rows || h.getColumns() != columns) {
273 try {
274 h.resize(rows, columns);
275 } catch (final WrongSizeException ignore) {
276 // never happens
277 }
278 }
279
280 for (var i = 0; i < rows; i++) {
281 for (var j = 0; j < columns; j++) {
282 if (i >= j) {
283 h.setElementAt(i, j, qr.getElementAt(i, j));
284 } else {
285 h.setElementAt(i, j, 0.0);
286 }
287 }
288 }
289 }
290
291 /**
292 * Returns the Householder vectors.
293 *
294 * @return Lower trapezoidal matrix whose columns define the reflections.
295 * @throws NotAvailableException Exception thrown if attempting to call this
296 * method before computing QR decomposition. To avoid this exception call
297 * decompose() method first.
298 * @see #decompose()
299 */
300 public Matrix getH() throws NotAvailableException {
301 final var rows = inputMatrix.getRows();
302 final var columns = inputMatrix.getColumns();
303 Matrix out = null;
304 try {
305 out = new Matrix(rows, columns);
306 } catch (final WrongSizeException ignore) {
307 // never happens
308 }
309 getH(out);
310 return out;
311 }
312
313 /**
314 * Computes upper triangular factor matrix and stores it into provided
315 * matrix.
316 * QR decomposition decomposes input matrix into Q (orthogonal matrix) and
317 * R, which is an upper triangular matrix.
318 *
319 * @param r Upper triangular factor matrix
320 * @throws NotAvailableException Exception thrown if attempting to call this
321 * method before computing QR decomposition. To avoid this exception call
322 * decompose() method first.
323 * @see #decompose()
324 */
325 public void getR(final Matrix r) throws NotAvailableException {
326 if (!isDecompositionAvailable()) {
327 throw new NotAvailableException();
328 }
329 final var columns = inputMatrix.getColumns();
330 final var rows = inputMatrix.getRows();
331
332 if (r.getRows() != columns || r.getColumns() != columns) {
333 try {
334 r.resize(columns, columns);
335 } catch (final WrongSizeException ignore) {
336 // never happens
337 }
338 }
339
340 for (var i = 0; i < columns; i++) {
341 if (i < rows) {
342 for (var j = 0; j < columns; j++) {
343 if (i < j) {
344 r.setElementAt(i, j, qr.getElementAt(i, j));
345 } else if (i == j) {
346 r.setElementAt(i, j, rDiag[i]);
347 } else {
348 r.setElementAt(i, j, 0.0);
349 }
350 }
351 } else {
352 for (var j = 0; j < columns; j++) {
353 r.setElementAt(i, j, 0.0);
354 }
355 }
356 }
357 }
358
359 /**
360 * Return upper triangular factor matrix.
361 * QR decomposition decomposes input matrix into Q (orthogonal matrix) and
362 * R, which is an upper triangular matrix.
363 *
364 * @return Upper triangular factor matrix
365 * @throws NotAvailableException Exception thrown if attempting to call this
366 * method before computing QR decomposition. To avoid this exception call
367 * decompose() method first.
368 * @see #decompose()
369 */
370 public Matrix getR() throws NotAvailableException {
371 final var columns = inputMatrix.getColumns();
372 Matrix out = null;
373 try {
374 out = new Matrix(columns, columns);
375 } catch (final WrongSizeException ignore) {
376 // never happens
377 }
378 getR(out);
379 return out;
380 }
381
382 /**
383 * Computes the economy-sized orthogonal factor matrix and stores it into
384 * provided matrix.
385 * QR decomposition decomposes input matrix into Q, which is an orthogonal
386 * matrix and R (upper triangular matrix).
387 *
388 * @param q Orthogonal factor matrix.
389 * @throws NotAvailableException Exception thrown if attempting to call
390 * @throws WrongSizeException Exception thrown if provided input matrix has
391 * less rows than columns.
392 * @see #decompose()
393 */
394 public void getQ(final Matrix q) throws NotAvailableException, WrongSizeException {
395 if (!isDecompositionAvailable()) {
396 throw new NotAvailableException();
397 }
398
399 final var rows = inputMatrix.getRows();
400 final var columns = inputMatrix.getColumns();
401 double s;
402
403 if (rows < columns) {
404 throw new WrongSizeException();
405 }
406
407 if (q.getRows() != rows || q.getColumns() != columns) {
408 try {
409 q.resize(rows, columns);
410 } catch (final WrongSizeException ignore) {
411 // never happens
412 }
413 }
414
415 for (var k = columns - 1; k >= 0; k--) {
416 for (var i = 0; i < rows; i++) {
417 q.setElementAt(i, k, 0.0);
418 }
419 q.setElementAt(k, k, 1.0);
420 for (var j = k; j < columns; j++) {
421 if (qr.getElementAt(k, k) != 0) {
422 s = 0.0;
423 for (var i = k; i < rows; i++) {
424 s += qr.getElementAt(i, k) * q.getElementAt(i, j);
425 }
426
427 s = -s / qr.getElementAt(k, k);
428 for (var i = k; i < rows; i++) {
429 q.setElementAt(i, j, q.getElementAt(i, j) + s * qr.getElementAt(i, k));
430 }
431 }
432 }
433 }
434 }
435
436 /**
437 * Return the economy-sized orthogonal factor matrix.
438 * QR decomposition decomposes input matrix into Q, which is an orthogonal
439 * matrix and R (upper triangular matrix).
440 *
441 * @return Orthogonal factor matrix.
442 * @throws NotAvailableException Exception thrown if attempting to call
443 * @throws WrongSizeException Exception thrown if provided input matrix has
444 * fewer rows than columns.
445 * @see #decompose()
446 */
447 public Matrix getQ() throws NotAvailableException, WrongSizeException {
448 final var rows = inputMatrix.getRows();
449 final var columns = inputMatrix.getColumns();
450
451 final var out = new Matrix(rows, columns);
452 getQ(out);
453 return out;
454 }
455
456 /**
457 * Solves a linear system of equations of the following form:
458 * A * X = B, where A is the input matrix provided for QR decomposition,
459 * X is the solution to the system of equations, and B is the parameters
460 * vector/matrix.
461 * Note: This method can be reused for different b vector/matrices without
462 * having to recompute QR decomposition on the same input matrix.
463 * Note: Provided b matrix must have the same number of rows as provided
464 * input matrix A, otherwise a WrongSizeException will be raised.
465 * Note: Provided input matrix "A" must have at least as many rows as columns,
466 * otherwise a WrongSizeException will be raised as well. For input matrices
467 * having a higher number of rows than columns, the system of equations will
468 * be overdetermined and the least squares solution will be found.
469 * Note: If provided input matrix A is rank deficient, a
470 * RankDeficientMatrixException will be thrown.
471 * Note: In order to execute this method, a QR decomposition must be
472 * available, otherwise a NotAvailableException will be raised. In order to
473 * avoid this exception call decompose() method first.
474 *
475 * @param b Parameters matrix that determine a linear system of equations.
476 * Provided matrix must have the same number of rows as provided input
477 * matrix for QR decomposition. Besides, each column on parameters matrix
478 * will represent a new system of equations, whose solution will be returned
479 * on appropriate column as an output of this method.
480 * @param result Matrix containing solution of linear system of equations on
481 * each column for each column of provided matrix of parameters b. Provided
482 * matrix will be resized if needed
483 * @throws NotAvailableException Exception thrown if attempting to call this
484 * method before computing QR decomposition. To avoid this exception call
485 * decompose() method first.
486 * @throws WrongSizeException Exception thrown if attempting to call this
487 * method using an input matrix with fewer rows than columns; or if provided
488 * parameters matrix (b) does not have the same number of rows as input
489 * matrix being QR decomposed.
490 * @throws RankDeficientMatrixException Exception thrown if provided input
491 * matrix to be QR decomposed is rank deficient. In this case linear system
492 * of equations cannot be solved.
493 */
494 public void solve(final Matrix b, final Matrix result) throws NotAvailableException, WrongSizeException,
495 RankDeficientMatrixException {
496 solve(b, DEFAULT_ROUND_ERROR, result);
497 }
498
499 /**
500 * Solves a linear system of equations of the following form:
501 * A * X = B, where A is the input matrix provided for QR decomposition,
502 * X is the solution to the system of equations, and B is the parameters
503 * vector/matrix.
504 * Note: This method can be reused for different b vector/matrices without
505 * having to recompute QR decomposition on the same input matrix.
506 * Note: Provided b matrix must have the same number of rows as provided
507 * input matrix A, otherwise a WrongSizeException will be raised.
508 * Note: Provided input matrix "A" must have at least as many rows as columns,
509 * otherwise a WrongSizeException will be raised as well. For input matrices
510 * having a higher number of rows than columns, the system of equations will
511 * be overdetermined and the least squares solution will be found.
512 * Note: If provided input matrix A is rank deficient, a
513 * RankDeficientMatrixException will be thrown.
514 * Note: In order to execute this method, a QR decomposition must be
515 * available, otherwise a NotAvailableException will be raised. In order to
516 * avoid this exception call decompose() method first.
517 *
518 * @param b Parameters matrix that determine a linear system of equations.
519 * Provided matrix must have the same number of rows as provided input
520 * matrix for QR decomposition. Besides, each column on parameters matrix
521 * will represent a new system of equations, whose solution will be returned
522 * on appropriate column as an output of this method.
523 * @param roundingError threshold to determine whether matrix b has full rank or not.
524 * By default, this is typically a tiny value close to zero.
525 * @param result Matrix containing solution of linear system of equations on
526 * each column for each column of provided matrix of parameters b. Provided
527 * matrix will be resized if needed
528 * @throws NotAvailableException Exception thrown if attempting to call this
529 * method before computing QR decomposition. To avoid this exception call
530 * decompose() method first.
531 * @throws WrongSizeException Exception thrown if attempting to call this
532 * method using an input matrix with fewer rows than columns; or if provided
533 * parameters matrix (b) does not have the same number of rows as input
534 * matrix being QR decomposed.
535 * @throws RankDeficientMatrixException Exception thrown if provided input
536 * matrix to be QR decomposed is rank deficient. In this case linear system
537 * of equations cannot be solved.
538 * @throws IllegalArgumentException if provided rounding error is negative.
539 */
540 public void solve(final Matrix b, final double roundingError, final Matrix result)
541 throws NotAvailableException, WrongSizeException, RankDeficientMatrixException {
542
543 if (!isDecompositionAvailable()) {
544 throw new NotAvailableException();
545 }
546
547 final var rows = inputMatrix.getRows();
548 final var columns = inputMatrix.getColumns();
549 final var rowsB = b.getRows();
550 final var colsB = b.getColumns();
551 double s;
552
553 if (rowsB != rows) {
554 throw new WrongSizeException();
555 }
556
557 if (roundingError < MIN_ROUND_ERROR) {
558 throw new IllegalArgumentException();
559 }
560
561 if (rows < columns) {
562 throw new WrongSizeException();
563 }
564 if (!isFullRank(roundingError)) {
565 throw new RankDeficientMatrixException();
566 }
567
568 // Copy b into X
569 final var x = new Matrix(b);
570
571 // Compute Y = transpose(Q) * B
572 for (var k = 0; k < columns; k++) {
573 for (var j = 0; j < colsB; j++) {
574 s = 0.0;
575 for (var i = k; i < rows; i++) {
576 s += qr.getElementAt(i, k) * x.getElementAt(i, j);
577 }
578 s = -s / qr.getElementAt(k, k);
579 for (var i = k; i < rows; i++) {
580 x.setElementAt(i, j, x.getElementAt(i, j) + s * qr.getElementAt(i, k));
581 }
582 }
583 }
584
585 // Solve R * X = Y
586 for (var k = columns - 1; k >= 0; k--) {
587 for (var j = 0; j < colsB; j++) {
588 x.setElementAt(k, j, x.getElementAt(k, j) / rDiag[k]);
589 }
590 for (var i = 0; i < k; i++) {
591 for (var j = 0; j < colsB; j++) {
592 x.setElementAt(i, j, x.getElementAt(i, j) - x.getElementAt(k, j) * qr.getElementAt(i, k));
593 }
594 }
595 }
596
597 // Pick only first columns rows of X in case of overdetermined systems
598 // (where rows > columns), otherwise rows == columns and we pick them all
599 if (result.getRows() != columns || result.getColumns() != colsB) {
600 // resize result
601 result.resize(columns, colsB);
602 }
603 result.setSubmatrix(0, 0, columns - 1, colsB - 1, x,
604 0, 0, columns - 1,
605 colsB - 1);
606 }
607
608 /**
609 * Solves a linear system of equations of the following form:
610 * A * X = B, where A is the input matrix provided for QR decomposition,
611 * X is the solution to the system of equations, and B is the parameters
612 * vector/matrix.
613 * Note: This method can be reused for different b vector/matrices without
614 * having to recompute QR decomposition on the same input matrix.
615 * Note: Provided b matrix must have the same number of rows as provided
616 * input matrix A, otherwise a WrongSizeException will be raised.
617 * Note: Provided input matrix "A" must have at least as many rows as columns,
618 * otherwise a WrongSizeException will be raised as well. For input matrices
619 * having a higher number of rows than columns, the system of equations will
620 * be overdetermined and the least squares solution will be found.
621 * Note: If provided input matrix A is rank deficient, a
622 * RankDeficientMatrixException will be thrown.
623 * Note: In order to execute this method, a QR decomposition must be
624 * available, otherwise a NotAvailableException will be raised. In order to
625 * avoid this exception call decompose() method first.
626 *
627 * @param b Parameters matrix that determine a linear system of equations.
628 * Provided matrix must have the same number of rows as provided input
629 * matrix for QR decomposition. Besides, each column on parameters matrix
630 * will represent a new system of equations, whose solution will be returned
631 * on appropriate column as an output of this method.
632 * @return Matrix containing solution of linear system of equations on each
633 * column for each column of provided matrix of parameters b.
634 * @throws NotAvailableException Exception thrown if attempting to call this
635 * method before computing QR decomposition. To avoid this exception call
636 * decompose() method first.
637 * @throws WrongSizeException Exception thrown if attempting to call this
638 * method using an input matrix with fewer rows than columns; or if provided
639 * parameters matrix (b) does not have the same number of rows as input
640 * matrix being QR decomposed.
641 * @throws RankDeficientMatrixException Exception thrown if provided input
642 * matrix to be QR decomposed is rank deficient. In this case linear system
643 * of equations cannot be solved.
644 */
645 public Matrix solve(final Matrix b) throws NotAvailableException, WrongSizeException, RankDeficientMatrixException {
646 return solve(b, DEFAULT_ROUND_ERROR);
647 }
648
649 /**
650 * Solves a linear system of equations of the following form:
651 * A * X = B, where A is the input matrix provided for QR decomposition,
652 * X is the solution to the system of equations, and B is the parameters
653 * vector/matrix.
654 * Note: This method can be reused for different b vector/matrices without
655 * having to recompute QR decomposition on the same input matrix.
656 * Note: Provided b matrix must have the same number of rows as provided
657 * input matrix A, otherwise a WrongSizeException will be raised.
658 * Note: Provided input matrix "A" must have at least as many rows as columns,
659 * otherwise a WrongSizeException will be raised as well. For input matrices
660 * having a higher number of rows than columns, the system of equations will
661 * be overdetermined and the least squares solution will be found.
662 * Note: If provided input matrix A is rank deficient, a
663 * RankDeficientMatrixException will be thrown.
664 * Note: In order to execute this method, a QR decomposition must be
665 * available, otherwise a NotAvailableException will be raised. In order to
666 * avoid this exception call decompose() method first.
667 *
668 * @param b Parameters matrix that determine a linear system of equations.
669 * Provided matrix must have the same number of rows as provided input
670 * matrix for QR decomposition. Besides, each column on parameters matrix
671 * will represent a new system of equations, whose solution will be returned
672 * on appropriate column as an output of this method.
673 * @param roundingError Determines the amount of margin given to determine
674 * whether a matrix has full rank or not due to rounding errors. If not
675 * provided, by default rounding error is set to zero, but this value can be
676 * relaxed if needed.
677 * @return Matrix containing solution of linear system of equations on each
678 * column for each column of provided matrix of parameters b.
679 * @throws NotAvailableException Exception thrown if attempting to call this
680 * method before computing QR decomposition. To avoid this exception call
681 * decompose() method first.
682 * @throws WrongSizeException Exception thrown if attempting to call this
683 * method using an input matrix with less rows than columns; or if provided
684 * parameters matrix (b) does not have the same number of rows as input
685 * matrix being QR decomposed.
686 * @throws RankDeficientMatrixException Exception thrown if provided input
687 * matrix to be QR decomposed is rank deficient. In this case linear system
688 * of equations cannot be solved.
689 * @throws IllegalArgumentException Exception thrown if provided rounding
690 * error is lower than minimum allowed value (MIN_ROUND_ERROR)
691 * @see #decompose()
692 */
693 public Matrix solve(final Matrix b, final double roundingError) throws NotAvailableException, WrongSizeException,
694 RankDeficientMatrixException {
695
696 final var columns = inputMatrix.getColumns();
697 final var colsB = b.getColumns();
698 final var out = new Matrix(columns, colsB);
699 solve(b, roundingError, out);
700 return out;
701 }
702 }