1 // ---------------------------------------------------------------------
2 //
3 // Copyright (C) 2018 - 2020 by the deal.II authors
4 //
5 // This file is part of the deal.II library.
6 //
7 // The deal.II library is free software; you can use it, redistribute
8 // it, and/or modify it under the terms of the GNU Lesser General
9 // Public License as published by the Free Software Foundation; either
10 // version 2.1 of the License, or (at your option) any later version.
11 // The full text of the license can be found in the file LICENSE.md at
12 // the top level directory of deal.II.
13 //
14 // ---------------------------------------------------------------------
15
16 #ifndef dealii_qr_h
17 #define dealii_qr_h
18
19 #include <deal.II/base/config.h>
20
21 #include <deal.II/base/exceptions.h>
22
23 #include <deal.II/lac/lapack_full_matrix.h>
24 #include <deal.II/lac/utilities.h>
25
26 #include <boost/signals2/signal.hpp>
27
28 #include <memory>
29
30 DEAL_II_NAMESPACE_OPEN
31
32 /**
33 * A base class for thin QR implementations.
34 *
35 * This class and classes derived from it are meant to build $Q$ and $R$
36 * matrices one row/column at a time, i.e., by growing $R$ matrix from an empty
37 * $0\times 0$ matrix to $N\times N$, where $N$ is the number of added column
38 * vectors.
39 *
40 * As a consequence, matrices which have the same number of rows as each vector
41 * (i.e. $Q$ matrix) is stored as a collection of vectors of `VectorType`.
42 */
43 template <typename VectorType>
44 class BaseQR
45 {
46 /**
47 * Number type for R matrix.
48 */
49 using Number = typename VectorType::value_type;
50
51 protected:
52 /**
53 * Default private constructor.
54 */
55 BaseQR();
56
57 public:
58 /**
59 * Destructor.
60 */
61 virtual ~BaseQR() = default;
62
63 /**
64 * Append @p column to the QR factorization.
65 * Returns <code>true</code> if the result is successful, i.e.
66 * the columns are linearly independent. Otherwise the @p column
67 * is rejected and the return value is <code>false</code>.
68 */
69 virtual bool
70 append_column(const VectorType &column) = 0;
71
72 /**
73 * Remove a column @p k and update QR factorization.
74 */
75 virtual void
76 remove_column(const unsigned int k = 0) = 0;
77
78 /**
79 * Return size of the subspace.
80 */
81 unsigned int
82 size() const;
83
84 /**
85 * Return the current upper triangular matrix R.
86 */
87 const LAPACKFullMatrix<Number> &
88 get_R() const;
89
90 /**
91 * Solve $Rx=y$. Vectors @p x and @p y should be consistent
92 * with the current size of the subspace.
93 * If @p transpose is <code>true</code>, $R^Tx=y$ is solved instead.
94 */
95 void
96 solve(Vector<Number> & x,
97 const Vector<Number> &y,
98 const bool transpose = false) const;
99
100 /**
101 * Set $y = Qx$. The size of $x$ should be consistent with the
102 * size of the R matrix.
103 */
104 virtual void
105 multiply_with_Q(VectorType &y, const Vector<Number> &x) const = 0;
106
107 /**
108 * Set $y = Q^Tx$. The size of $x$ should be consistent with the
109 * size of column vectors.
110 */
111 virtual void
112 multiply_with_QT(Vector<Number> &y, const VectorType &x) const = 0;
113
114 /**
115 * Set $y = QRx$. The size of $x$ should be consistent with the
116 * size of the R matrix.
117 */
118 virtual void
119 multiply_with_A(VectorType &y, const Vector<Number> &x) const = 0;
120
121 /**
122 * Set $y = R^T Q^Tx$. The size of $x$ should be consistent with the
123 * size of column vectors.
124 */
125 virtual void
126 multiply_with_AT(Vector<Number> &y, const VectorType &x) const = 0;
127
128 /**
129 * Connect a slot to retrieve a notification when the Givens rotations
130 * are performed.
131 *
132 * The function takes two indices, @p i and @p j, describing the plane of
133 * rotation, and a triplet of numbers @p csr (cosine, sine and radius, see
134 * Utilities::LinearAlgebra::givens_rotation()) which represents the rotation
135 * matrix.
136 */
137 boost::signals2::connection
138 connect_givens_slot(
139 const std::function<void(const unsigned int i,
140 const unsigned int j,
141 const std::array<Number, 3> &csr)> &slot);
142
143 protected:
144 /**
145 * Compute $y=Hx$ where $H$ is the matrix formed by the column vectors stored
146 * by this object.
147 */
148 void
149 multiply_with_cols(VectorType &y, const Vector<Number> &x) const;
150
151 /**
152 * Multiply with transpose columns stored in the object.
153 */
154 void
155 multiply_with_colsT(Vector<Number> &y, const VectorType &x) const;
156
157 /**
158 * A vector of unique pointers to store columns.
159 */
160 std::vector<std::unique_ptr<VectorType>> columns;
161
162 /**
163 * Matrix to store R.
164 */
165 LAPACKFullMatrix<Number> R;
166
167 /**
168 * Current size (number of columns in Q).
169 */
170 unsigned int current_size;
171
172 /**
173 * Signal used to retrieve a notification
174 * when Givens rotations are performed in the `(i,j)`-plane.
175 */
176 boost::signals2::signal<void(const unsigned int i,
177 const unsigned int j,
178 const std::array<Number, 3> &)>
179 givens_signal;
180 };
181
182 // clang-format off
183 /**
184 * A class to compute and store the QR factorization of a matrix represented by a set of column vectors.
185 *
186 * The class is design to update a given (possibly empty) QR factorization
187 * of a matrix $A$ (constructed incrementally by providing its columns)
188 * due to the addition of a new column vector to $A$. This is equivalent to
189 * constructing an orthonormal basis by the Gram-Schmidt procedure.
190 * The class also provides update functionality when the first column
191 * is removed.
192 *
193 * The `VectorType` template argument may either be a parallel and serial vector, and only need
194 * to have basic operations such as additions, scalar product, etc.
195 * It also needs to have a copy-constructor.
196 *
197 * See sections 6.5.2-6.5.3 on pp. 335-338 in
198 * @code{.bib}
199 * @Book{Golub2013,
200 * title = {Matrix computations},
201 * publisher = {Johns Hopkins University Press},
202 * year = {2013},
203 * author = {Golub, Gene H and Van Loan, Charles F},
204 * edition = {4},
205 * }
206 * @endcode
207 * as well as
208 * @code{.bib}
209 * @article{Daniel1976,
210 * author = {Daniel, James W and Gragg, Walter Bill and Kaufman, Linda and Stewart, Gilbert W},
211 * title = {{Reorthogonalization and stable algorithms for updating the Gram-Schmidt QR factorization}},
212 * journal = {Mathematics of Computation},
213 * year = {1976},
214 * volume = {30},
215 * number = {136},
216 * pages = {772--795},
217 * }
218 * @Article{Reichel1990,
219 * author = {Reichel, L. and Gragg, W. B.},
220 * title = {{Algorithm 686: FORTRAN Subroutines for Updating the QR Decomposition}},
221 * journal = {ACM Trans. Math. Softw.},
222 * year = {1990},
223 * volume = {16},
224 * number = {4},
225 * pages = {369--377},
226 * month = dec,
227 * issn = {0098-3500},
228 * acmid = {98291},
229 * address = {New York, NY, USA},
230 * doi = {10.1145/98267.98291},
231 * issue_date = {Dec. 1990},
232 * numpages = {9},
233 * publisher = {ACM},
234 * url = {http://doi.acm.org/10.1145/98267.98291},
235 * }
236 * @endcode
237 */
238 // clang-format on
239 template <typename VectorType>
240 class QR : public BaseQR<VectorType>
241 {
242 public:
243 /**
244 * Number type for R matrix.
245 */
246 using Number = typename VectorType::value_type;
247
248 /**
249 * Default constructor.
250 */
251 QR();
252
253 /**
254 * Destructor.
255 */
256 virtual ~QR() = default;
257
258 /**
259 * @copydoc BaseQR::append_column
260 *
261 * @note Currently this function always returns <code>true</code>.
262 */
263 virtual bool
264 append_column(const VectorType &column);
265
266 /**
267 * Remove first column and update QR factorization.
268 *
269 * Starting from the given QR decomposition
270 * $QR= A = [a_1\,\dots a_n], \quad a_i \in {\mathbb R}^m$
271 * we aim at computing factorization of
272 * $\tilde Q \tilde R= \tilde A = [a_2\,\dots a_n], \quad a_i \in {\mathbb
273 * R}^m$.
274 *
275 * The standard approach is to partition $R$ as
276 * \f[
277 * R =
278 * \begin{bmatrix}
279 * r_{11} & w^T \\
280 * 0 & R_{33}
281 * \end{bmatrix}
282 * \f]
283 * It then follows that
284 * \f[
285 * Q^T \tilde A =
286 * \begin{bmatrix}
287 * 0 & w^T \\
288 * 0 & R_{33}
289 * \end{bmatrix}
290 * \f]
291 * is upper Hessenberg where unwanted sub-diagonal elements can be
292 * zeroed by a sequence of Givens rotations.
293 *
294 * Note that $\tilde R^T \tilde R = \tilde A^T \tilde A$,
295 * where the RHS is included in $A^T A = R^T R$. Therefore
296 * $\tilde R$ can be obtained by Cholesky decomposition.
297 */
298 virtual void
299 remove_column(const unsigned int k = 0);
300
301 virtual void
302 multiply_with_Q(VectorType &y, const Vector<Number> &x) const;
303
304 virtual void
305 multiply_with_QT(Vector<Number> &y, const VectorType &x) const;
306
307 virtual void
308 multiply_with_A(VectorType &y, const Vector<Number> &x) const;
309
310 virtual void
311 multiply_with_AT(Vector<Number> &y, const VectorType &x) const;
312
313 private:
314 /**
315 * Apply givens rotation in the `(i,j)`-plane to @p Q and @p R so that
316 * <code>R(k,k)</code> is zeroed.
317 *
318 * See Chapter 5.1.9 of Golub 2013, Matrix computations.
319 */
320 void
321 apply_givens_rotation(const unsigned int i, const unsigned int k);
322
323 /**
324 * Temporary vector needed to do Givens rotation of Q.
325 */
326 VectorType tmp;
327 };
328
329
330
331 /**
332 * A class to obtain the triangular $R$ matrix of the $A=QR$ factorization
333 * together with the matrix $A$ itself. The orthonormal matrix $Q$ is not stored
334 * explicitly, the name of the class.
335 * The multiplication with $Q$ can be represented as $Q=A R^{-1}$, whereas the
336 * multiplication with $Q^T$ is given by $Q^T=R^{-T}A^T$.
337 *
338 * The class is designed to update a given (possibly empty) QR factorization
339 * due to the addition of a new column vector. This is equivalent to
340 * constructing an orthonormal basis by the Gram-Schmidt procedure.
341 * The class also provides update functionality when the column
342 * is removed.
343 *
344 * The `VectorType` template argument may either be a parallel and serial
345 * vector, and only need to have basic operations such as additions, scalar
346 * product, etc. It also needs to have a copy-constructor.
347 */
348 template <typename VectorType>
349 class ImplicitQR : public BaseQR<VectorType>
350 {
351 public:
352 /**
353 * Number type for R matrix.
354 */
355 using Number = typename VectorType::value_type;
356
357 /**
358 * Default constructor.
359 */
360 ImplicitQR();
361
362 /**
363 * Destructor.
364 */
365 virtual ~ImplicitQR() = default;
366
367 virtual bool
368 append_column(const VectorType &column);
369
370 /**
371 * Remove column and update QR factorization.
372 *
373 * Starting from the given QR decomposition
374 * $QR= A = [a_1\,\dots a_n], \quad a_i \in R^m$
375 * we aim at computing factorization of
376 * $\tilde Q \tilde R= \tilde A = [a_2\,\dots a_n], \quad a_i \in R^m$.
377 *
378 * Note that $\tilde R^T \tilde R = \tilde A^T \tilde A$,
379 * where the RHS is included in $A^T A = R^T R$. Therefore
380 * $\tilde R$ can be obtained by Cholesky decomposition.
381 */
382 virtual void
383 remove_column(const unsigned int k = 0);
384
385 virtual void
386 multiply_with_Q(VectorType &y, const Vector<Number> &x) const;
387
388 virtual void
389 multiply_with_QT(Vector<Number> &y, const VectorType &x) const;
390
391 virtual void
392 multiply_with_A(VectorType &y, const Vector<Number> &x) const;
393
394 virtual void
395 multiply_with_AT(Vector<Number> &y, const VectorType &x) const;
396
397 /**
398 * Connect a slot to implement a custom check of linear dependency
399 * during addition of a column.
400 *
401 * Here, @p u is the last column of the to-be R matrix, @p rho
402 * is its diagonal and @p col_norm_sqr is the square of the $l2$ norm of the column.
403 * The function should return <code>true</code> if the new column is
404 * linearly independent.
405 */
406 boost::signals2::connection
407 connect_append_column_slot(
408 const std::function<bool(const Vector<Number> &u,
409 const Number & rho2,
410 const Number & col_norm_sqr)> &slot);
411
412 private:
413 /**
414 * Apply givens rotation in the `(i,k)`-plane to zero out $R(k,k)$.
415 */
416 void
417 apply_givens_rotation(const unsigned int i, const unsigned int k);
418
419 /**
420 * Signal used to decide if the new column is linear dependent.
421 *
422 * Here, @p u is the last column of the to-be R matrix, @p rho
423 * is its diagonal and @p col_norm_sqr is the square of the $l2$ norm of the column.
424 * The function should return <code>true</code> if the new column is
425 * linearly independent.
426 */
427 boost::signals2::signal<bool(const Vector<Number> &u,
428 const Number & rho,
429 const Number & col_norm_sqr)>
430 column_signal;
431 };
432
433 // ------------------- inline and template functions ----------------
434 #ifndef DOXYGEN
435
436 namespace internal
437 {
438 namespace QRImplementation
439 {
440 // We want to avoid including our own LAPACK wrapper header in any external
441 // headers to avoid possible conflicts with other packages that may define
442 // their own such header. At the same time we want to be able to call some
443 // LAPACK functions from the template functions below. To resolve both
444 // problems define some extra wrappers here that can be in the header:
445 template <typename Number>
446 void
447 call_trmv(const char uplo,
448 const char trans,
449 const char diag,
450 const types::blas_int n,
451 const Number * a,
452 const types::blas_int lda,
453 Number * x,
454 const types::blas_int incx);
455
456 template <typename Number>
457 void
458 call_trtrs(const char uplo,
459 const char trans,
460 const char diag,
461 const types::blas_int n,
462 const types::blas_int nrhs,
463 const Number * a,
464 const types::blas_int lda,
465 Number * b,
466 const types::blas_int ldb,
467 types::blas_int * info);
468 } // namespace QRImplementation
469 } // namespace internal
470
471
472
473 template <typename VectorType>
BaseQR()474 BaseQR<VectorType>::BaseQR()
475 : current_size(0)
476 {
477 R.set_property(LAPACKSupport::upper_triangular);
478 }
479
480
481
482 template <typename VectorType>
483 unsigned int
size()484 BaseQR<VectorType>::size() const
485 {
486 return current_size;
487 }
488
489
490
491 template <typename VectorType>
492 const LAPACKFullMatrix<typename BaseQR<VectorType>::Number> &
get_R()493 BaseQR<VectorType>::get_R() const
494 {
495 return R;
496 }
497
498
499
500 template <typename VectorType>
501 void
solve(Vector<Number> & x,const Vector<Number> & y,const bool transpose)502 BaseQR<VectorType>::solve(Vector<Number> & x,
503 const Vector<Number> &y,
504 const bool transpose) const
505 {
506 Assert(x.size() == this->current_size,
507 ExcDimensionMismatch(x.size(), this->current_size));
508 Assert(y.size() == this->current_size,
509 ExcDimensionMismatch(y.size(), this->current_size));
510
511 // copy if the two vectors are not the same
512 if (&x != &y)
513 x = y;
514
515 const int lda = this->current_size;
516 const int ldb = this->current_size;
517 const int N = this->current_size;
518 const int n_rhs = 1;
519 int info = 0;
520 internal::QRImplementation::call_trtrs('U',
521 transpose ? 'T' : 'N',
522 'N',
523 N,
524 n_rhs,
525 &this->R(0, 0),
526 lda,
527 &x(0),
528 ldb,
529 &info);
530 }
531
532
533
534 template <typename VectorType>
535 void
multiply_with_cols(VectorType & y,const Vector<Number> & x)536 BaseQR<VectorType>::multiply_with_cols(VectorType & y,
537 const Vector<Number> &x) const
538 {
539 Assert(x.size() == this->current_size,
540 ExcDimensionMismatch(x.size(), this->current_size));
541
542 y = 0.;
543 for (unsigned int j = 0; j < this->current_size; ++j)
544 y.add(x[j], *this->columns[j]);
545 }
546
547
548
549 template <typename VectorType>
550 void
multiply_with_colsT(Vector<Number> & y,const VectorType & x)551 BaseQR<VectorType>::multiply_with_colsT(Vector<Number> & y,
552 const VectorType &x) const
553 {
554 Assert(y.size() == this->current_size,
555 ExcDimensionMismatch(y.size(), this->current_size));
556
557 for (unsigned int j = 0; j < this->current_size; ++j)
558 y[j] = (*this->columns[j]) * x;
559 }
560
561
562
563 template <class VectorType>
564 boost::signals2::connection
connect_givens_slot(const std::function<void (const unsigned int i,const unsigned int j,const std::array<Number,3> &)> & slot)565 BaseQR<VectorType>::connect_givens_slot(
566 const std::function<void(const unsigned int i,
567 const unsigned int j,
568 const std::array<Number, 3> &)> &slot)
569 {
570 return givens_signal.connect(slot);
571 }
572
573
574
575 template <class VectorType>
576 boost::signals2::connection
connect_append_column_slot(const std::function<bool (const Vector<Number> & u,const Number & rho,const Number & col_norm_sqr)> & slot)577 ImplicitQR<VectorType>::connect_append_column_slot(
578 const std::function<bool(const Vector<Number> &u,
579 const Number & rho,
580 const Number & col_norm_sqr)> &slot)
581 {
582 return column_signal.connect(slot);
583 }
584
585
586
587 template <typename VectorType>
ImplicitQR()588 ImplicitQR<VectorType>::ImplicitQR()
589 : BaseQR<VectorType>()
590 {}
591
592
593
594 template <typename VectorType>
595 bool
append_column(const VectorType & column)596 ImplicitQR<VectorType>::append_column(const VectorType &column)
597 {
598 if (this->current_size == 0)
599 {
600 this->R.grow_or_shrink(this->current_size + 1);
601 this->columns.push_back(std::make_unique<VectorType>(column));
602 this->R(0, 0) = column.l2_norm();
603 ++this->current_size;
604 }
605 else
606 {
607 // first get scalar products with A^T
608 Vector<Number> u(this->current_size);
609 this->multiply_with_AT(u, column);
610
611 // now solve R^T x = (A^T * column)
612 const int lda = this->current_size;
613 const int ldb = this->current_size;
614 const int N = this->current_size;
615 const int n_rhs = 1;
616 int info = 0;
617 internal::QRImplementation::call_trtrs(
618 'U', 'T', 'N', N, n_rhs, &this->R(0, 0), lda, &u(0), ldb, &info);
619
620 // finally get the diagonal element:
621 // rho2 = |column|^2 - |u|^2
622 const Number column_norm_sqr = column.norm_sqr();
623 const Number rho2 = column_norm_sqr - u.norm_sqr();
624 const bool linearly_independent =
625 column_signal.empty() ? rho2 > 0 :
626 column_signal(u, rho2, column_norm_sqr).get();
627
628 // bail out if it turns out to be linearly dependent
629 if (!linearly_independent)
630 return false;
631
632 // at this point we update is successful and we can enlarge R
633 // and store the column:
634 this->columns.push_back(std::make_unique<VectorType>(column));
635 this->R.grow_or_shrink(this->current_size + 1);
636 this->R(this->current_size, this->current_size) = std::sqrt(rho2);
637 for (unsigned int i = 0; i < this->current_size; ++i)
638 this->R(i, this->current_size) = u(i);
639
640 this->current_size++;
641 }
642
643 return true;
644 }
645
646
647
648 template <typename VectorType>
649 void
apply_givens_rotation(const unsigned int i,const unsigned int k)650 ImplicitQR<VectorType>::apply_givens_rotation(const unsigned int i,
651 const unsigned int k)
652 {
653 AssertIndexRange(i, k);
654 AssertIndexRange(k, this->current_size);
655 const std::array<Number, 3> csr =
656 dealii::Utilities::LinearAlgebra::givens_rotation<Number>(this->R(i, k),
657 this->R(k, k));
658
659 // first, set k'th column:
660 this->R(i, k) = csr[2];
661 this->R(k, k) = 0.;
662 // now do the rest:
663 for (unsigned int j = 0; j < this->R.n(); ++j)
664 if (j != k)
665 {
666 const Number t = this->R(i, j);
667 this->R(i, j) = csr[0] * this->R(i, j) + csr[1] * this->R(k, j);
668 this->R(k, j) = -csr[1] * t + csr[0] * this->R(k, j);
669 }
670
671 if (!this->givens_signal.empty())
672 this->givens_signal(i, k, csr);
673 }
674
675
676
677 template <typename VectorType>
678 void
remove_column(const unsigned int k)679 ImplicitQR<VectorType>::remove_column(const unsigned int k)
680 {
681 // before actually removing a column from Q and resizing R,
682 // apply givens rotations to bring H into upper triangular form:
683 for (unsigned int j = k + 1; j < this->R.n(); ++j)
684 {
685 const unsigned int i = j - 1;
686 apply_givens_rotation(i, j);
687 }
688
689 // remove last row and k-th column
690 --this->current_size;
691 this->R.remove_row_and_column(this->current_size, k);
692
693 // Finally remove the column from A
694 this->columns.erase(this->columns.begin() + k);
695 }
696
697
698
699 template <typename VectorType>
700 void
multiply_with_Q(VectorType & y,const Vector<Number> & x)701 ImplicitQR<VectorType>::multiply_with_Q(VectorType & y,
702 const Vector<Number> &x) const
703 {
704 // A = QR
705 // A R^{-1} = Q
706 Vector<Number> x1 = x;
707 BaseQR<VectorType>::solve(x1, x1, false);
708 multiply_with_A(y, x1);
709 }
710
711
712
713 template <typename VectorType>
714 void
multiply_with_QT(Vector<Number> & y,const VectorType & x)715 ImplicitQR<VectorType>::multiply_with_QT(Vector<Number> & y,
716 const VectorType &x) const
717 {
718 // A = QR
719 // A^T = R^T Q^T
720 // {R^T}^{-1} A^T = Q^T
721 multiply_with_AT(y, x);
722 BaseQR<VectorType>::solve(y, y, true);
723 }
724
725
726
727 template <typename VectorType>
728 void
multiply_with_A(VectorType & y,const Vector<Number> & x)729 ImplicitQR<VectorType>::multiply_with_A(VectorType & y,
730 const Vector<Number> &x) const
731 {
732 BaseQR<VectorType>::multiply_with_cols(y, x);
733 }
734
735
736
737 template <typename VectorType>
738 void
multiply_with_AT(Vector<Number> & y,const VectorType & x)739 ImplicitQR<VectorType>::multiply_with_AT(Vector<Number> & y,
740 const VectorType &x) const
741 {
742 BaseQR<VectorType>::multiply_with_colsT(y, x);
743 }
744
745
746
747 template <typename VectorType>
QR()748 QR<VectorType>::QR()
749 : BaseQR<VectorType>()
750 {}
751
752
753
754 template <typename VectorType>
755 bool
append_column(const VectorType & column)756 QR<VectorType>::append_column(const VectorType &column)
757 {
758 // resize R:
759 this->R.grow_or_shrink(this->current_size + 1);
760 this->columns.push_back(std::make_unique<VectorType>(column));
761
762 // now a Gram-Schmidt part: orthonormalize the new column
763 // against everything we have so far:
764 auto &last_col = *this->columns.back();
765 for (unsigned int i = 0; i < this->current_size; ++i)
766 {
767 const auto &i_col = *this->columns[i];
768 this->R(i, this->current_size) = i_col * last_col;
769 last_col.add(-this->R(i, this->current_size), i_col);
770 }
771
772 this->R(this->current_size, this->current_size) = last_col.l2_norm();
773
774 Assert(this->R(this->current_size, this->current_size) > 0.,
775 ExcDivideByZero());
776 last_col *= 1. / this->R(this->current_size, this->current_size);
777
778 ++this->current_size;
779 return true;
780 }
781
782
783
784 template <typename VectorType>
785 void
apply_givens_rotation(const unsigned int i,const unsigned int k)786 QR<VectorType>::apply_givens_rotation(const unsigned int i,
787 const unsigned int k)
788 {
789 AssertIndexRange(i, k);
790 AssertIndexRange(k, this->current_size);
791 const std::array<Number, 3> csr =
792 dealii::Utilities::LinearAlgebra::givens_rotation<Number>(this->R(i, k),
793 this->R(k, k));
794
795 // first, set k'th column:
796 this->R(i, k) = csr[2];
797 this->R(k, k) = 0.;
798 // now do the rest:
799 for (unsigned int j = 0; j < this->R.n(); ++j)
800 if (j != k)
801 {
802 const Number t = this->R(i, j);
803 this->R(i, j) = csr[0] * this->R(i, j) + csr[1] * this->R(k, j);
804 this->R(k, j) = -csr[1] * t + csr[0] * this->R(k, j);
805 }
806
807 // now adjust i,k columns due to multiplication with the
808 // transpose Givens matrix from right:
809 auto &col_i = *this->columns[i];
810 auto &col_k = *this->columns[k];
811 // save column i:
812 tmp = col_i;
813 col_i.sadd(csr[0], csr[1], col_k);
814 col_k.sadd(csr[0], -csr[1], tmp);
815
816 if (!this->givens_signal.empty())
817 this->givens_signal(i, k, csr);
818 }
819
820
821
822 template <typename VectorType>
823 void
remove_column(const unsigned int k)824 QR<VectorType>::remove_column(const unsigned int k)
825 {
826 AssertIndexRange(k, this->current_size);
827 Assert(this->current_size > 0,
828 ExcMessage("Can not remove a column if QR is empty"));
829 // apply a sequence of Givens rotations
830 // see section 6.5 "Updating matrix factorizations" in Golub 2013, Matrix
831 // computations
832
833 // So we want to have QR for \tilde A \in R^{m*(n-1)}
834 // if we remove the column k, we end up with upper Hessenberg matrix
835 // x x x x x
836 // x x x x
837 // H = x x x
838 // x x x
839 // x x
840 // x
841 // where k = 2 (3rd column), m = 7, n = 6
842 //
843 // before actually removing a column from Q and resizing R,
844 // apply givens rotations to bring H into upper triangular form:
845 for (unsigned int j = k + 1; j < this->R.n(); ++j)
846 {
847 const unsigned int i = j - 1;
848 apply_givens_rotation(i, j);
849 }
850
851 // now we can throw away the column from Q and adjust R
852 // since we do thin-QR, after Givens rotations we need to throw
853 // away the last column:
854 const unsigned int size_minus_1 = this->columns.size() - 1;
855 this->columns.erase(this->columns.begin() + size_minus_1);
856
857 // remove last row and k-th column
858 --this->current_size;
859 this->R.remove_row_and_column(this->current_size, k);
860 }
861
862
863
864 template <typename VectorType>
865 void
multiply_with_Q(VectorType & y,const Vector<Number> & x)866 QR<VectorType>::multiply_with_Q(VectorType &y, const Vector<Number> &x) const
867 {
868 BaseQR<VectorType>::multiply_with_cols(y, x);
869 }
870
871
872
873 template <typename VectorType>
874 void
multiply_with_QT(Vector<Number> & y,const VectorType & x)875 QR<VectorType>::multiply_with_QT(Vector<Number> &y, const VectorType &x) const
876 {
877 BaseQR<VectorType>::multiply_with_colsT(y, x);
878 }
879
880
881
882 template <typename VectorType>
883 void
multiply_with_A(VectorType & y,const Vector<Number> & x)884 QR<VectorType>::multiply_with_A(VectorType &y, const Vector<Number> &x) const
885 {
886 Vector<Number> x1 = x;
887 const int N = this->current_size;
888 const int lda = N;
889 const int incx = 1;
890 internal::QRImplementation::call_trmv(
891 'U', 'N', 'N', N, &this->R(0, 0), lda, &x1[0], incx);
892
893 multiply_with_Q(y, x1);
894 }
895
896
897
898 template <typename VectorType>
899 void
multiply_with_AT(Vector<Number> & y,const VectorType & x)900 QR<VectorType>::multiply_with_AT(Vector<Number> &y, const VectorType &x) const
901 {
902 multiply_with_QT(y, x);
903
904 const int N = this->current_size;
905 const int lda = N;
906 const int incx = 1;
907 internal::QRImplementation::call_trmv(
908 'U', 'T', 'N', N, &this->R(0, 0), lda, &y[0], incx);
909 }
910
911 #endif // no DOXYGEN
912
913 DEAL_II_NAMESPACE_CLOSE
914
915 #endif
916