deal.II version GIT relicensing-6839-g338455934c 2026-10-02 12:10:01+00:00
\(\newcommand{\dealvcentcolon}{\mathrel{\mathop{:}}}\) \(\newcommand{\dealcoloneq}{\dealvcentcolon\mathrel{\mkern-1.2mu}=}\) \(\newcommand{\jump}[1]{\left[\!\left[ #1 \right]\!\right]}\) \(\newcommand{\average}[1]{\left\{\!\left\{ #1 \right\}\!\right\}}\)
Loading...
Searching...
No Matches
qr.h
Go to the documentation of this file.
1// -----------------------------------------------------------------------------
2//
3// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception OR LGPL-2.1-or-later
4// Copyright (C) 2019 - 2025 by the deal.II authors
5//
6// This file is part of the deal.II library.
7//
8// Detailed license information governing the source code and contributions
9// can be found in LICENSE.md and CONTRIBUTING.md at the top level directory.
10//
11// -----------------------------------------------------------------------------
12
13#ifndef dealii_qr_h
14#define dealii_qr_h
15
16#include <deal.II/base/config.h>
17
19
22
23#include <boost/signals2/signal.hpp>
24
25#include <memory>
26
28
40template <typename VectorType>
41class BaseQR
42{
46 using Number = typename VectorType::value_type;
47
48protected:
53
54public:
58 virtual ~BaseQR() = default;
59
66 virtual bool
67 append_column(const VectorType &column) = 0;
68
72 virtual void
73 remove_column(const unsigned int k = 0) = 0;
74
78 unsigned int
79 size() const;
80
85 get_R() const;
86
92 void
94 const Vector<Number> &y,
95 const bool transpose = false) const;
96
101 virtual void
102 multiply_with_Q(VectorType &y, const Vector<Number> &x) const = 0;
103
108 virtual void
109 multiply_with_QT(Vector<Number> &y, const VectorType &x) const = 0;
110
115 virtual void
116 multiply_with_A(VectorType &y, const Vector<Number> &x) const = 0;
117
122 virtual void
123 multiply_with_AT(Vector<Number> &y, const VectorType &x) const = 0;
124
134 boost::signals2::connection
136 const std::function<void(const unsigned int i,
137 const unsigned int j,
138 const std::array<Number, 3> &csr)> &slot);
139
140protected:
145 void
146 multiply_with_cols(VectorType &y, const Vector<Number> &x) const;
147
151 void
152 multiply_with_colsT(Vector<Number> &y, const VectorType &x) const;
153
157 std::vector<std::unique_ptr<VectorType>> columns;
158
163
167 unsigned int current_size;
168
173 boost::signals2::signal<void(const unsigned int i,
174 const unsigned int j,
175 const std::array<Number, 3> &)>
177};
178
179// clang-format off
197// clang-format on
198template <typename VectorType>
199class QR : public BaseQR<VectorType>
200{
201public:
205 using Number = typename VectorType::value_type;
206
210 QR();
211
215 virtual ~QR() = default;
216
222 virtual bool
223 append_column(const VectorType &column) override;
224
257 virtual void
258 remove_column(const unsigned int k = 0) override;
259
260 virtual void
261 multiply_with_Q(VectorType &y, const Vector<Number> &x) const override;
262
263 virtual void
264 multiply_with_QT(Vector<Number> &y, const VectorType &x) const override;
265
266 virtual void
267 multiply_with_A(VectorType &y, const Vector<Number> &x) const override;
268
269 virtual void
270 multiply_with_AT(Vector<Number> &y, const VectorType &x) const override;
271
272private:
279 void
280 apply_givens_rotation(const unsigned int i, const unsigned int k);
281
285 VectorType tmp;
286};
287
288
289
307template <typename VectorType>
308class ImplicitQR : public BaseQR<VectorType>
309{
310public:
314 using Number = typename VectorType::value_type;
315
320
324 virtual ~ImplicitQR() = default;
325
326 virtual bool
327 append_column(const VectorType &column);
328
341 virtual void
342 remove_column(const unsigned int k = 0);
343
344 virtual void
345 multiply_with_Q(VectorType &y, const Vector<Number> &x) const;
346
347 virtual void
348 multiply_with_QT(Vector<Number> &y, const VectorType &x) const;
349
350 virtual void
351 multiply_with_A(VectorType &y, const Vector<Number> &x) const;
352
353 virtual void
354 multiply_with_AT(Vector<Number> &y, const VectorType &x) const;
355
365 boost::signals2::connection
367 const std::function<bool(const Vector<Number> &u,
368 const Number &rho2,
369 const Number &col_norm_sqr)> &slot);
370
371private:
375 void
376 apply_givens_rotation(const unsigned int i, const unsigned int k);
377
386 boost::signals2::signal<bool(const Vector<Number> &u,
387 const Number &rho,
388 const Number &col_norm_sqr)>
390};
391
392// ------------------- inline and template functions ----------------
393#ifndef DOXYGEN
394
395namespace internal
396{
397 namespace QRImplementation
398 {
399 // We want to avoid including our own LAPACK wrapper header in any external
400 // headers to avoid possible conflicts with other packages that may define
401 // their own such header. At the same time we want to be able to call some
402 // LAPACK functions from the template functions below. To resolve both
403 // problems define some extra wrappers here that can be in the header:
404 template <typename Number>
405 void
406 call_trmv(const char uplo,
407 const char trans,
408 const char diag,
409 const types::blas_int n,
410 const Number *a,
411 const types::blas_int lda,
412 Number *x,
413 const types::blas_int incx);
414
415 template <typename Number>
416 void
417 call_trtrs(const char uplo,
418 const char trans,
419 const char diag,
420 const types::blas_int n,
421 const types::blas_int nrhs,
422 const Number *a,
423 const types::blas_int lda,
424 Number *b,
425 const types::blas_int ldb,
426 types::blas_int *info);
427 } // namespace QRImplementation
428} // namespace internal
429
430
431
432template <typename VectorType>
434 : current_size(0)
435{
437}
438
439
440
441template <typename VectorType>
442unsigned int
444{
445 return current_size;
446}
447
448
449
450template <typename VectorType>
453{
454 return R;
455}
456
457
458
459template <typename VectorType>
460void
462 const Vector<Number> &y,
463 const bool transpose) const
464{
465 Assert(x.size() == this->current_size,
466 ExcDimensionMismatch(x.size(), this->current_size));
467 Assert(y.size() == this->current_size,
468 ExcDimensionMismatch(y.size(), this->current_size));
469
470 // copy if the two vectors are not the same
471 if (&x != &y)
472 x = y;
473
474 const int lda = this->current_size;
475 const int ldb = this->current_size;
476 const int N = this->current_size;
477 const int n_rhs = 1;
478 int info = 0;
480 transpose ? 'T' : 'N',
481 'N',
482 N,
483 n_rhs,
484 &this->R(0, 0),
485 lda,
486 &x(0),
487 ldb,
488 &info);
489}
490
491
492
493template <typename VectorType>
494void
496 const Vector<Number> &x) const
497{
498 Assert(x.size() == this->current_size,
499 ExcDimensionMismatch(x.size(), this->current_size));
500
501 y = 0.;
502 for (unsigned int j = 0; j < this->current_size; ++j)
503 y.add(x[j], *this->columns[j]);
504}
505
506
507
508template <typename VectorType>
509void
511 const VectorType &x) const
512{
513 Assert(y.size() == this->current_size,
514 ExcDimensionMismatch(y.size(), this->current_size));
515
516 for (unsigned int j = 0; j < this->current_size; ++j)
517 y[j] = (*this->columns[j]) * x;
518}
519
520
521
522template <typename VectorType>
523boost::signals2::connection
525 const std::function<void(const unsigned int i,
526 const unsigned int j,
527 const std::array<Number, 3> &)> &slot)
528{
529 return givens_signal.connect(slot);
530}
531
532
533
534template <typename VectorType>
535boost::signals2::connection
537 const std::function<bool(const Vector<Number> &u,
538 const Number &rho,
539 const Number &col_norm_sqr)> &slot)
540{
541 return column_signal.connect(slot);
542}
543
544
545
546template <typename VectorType>
548 : BaseQR<VectorType>()
549{}
550
551
552
553template <typename VectorType>
554bool
555ImplicitQR<VectorType>::append_column(const VectorType &column)
556{
557 if (this->current_size == 0)
558 {
559 this->R.grow_or_shrink(this->current_size + 1);
560 this->columns.push_back(std::make_unique<VectorType>(column));
561 this->R(0, 0) = column.l2_norm();
562 ++this->current_size;
563 }
564 else
565 {
566 // first get scalar products with A^T
567 Vector<Number> u(this->current_size);
568 this->multiply_with_AT(u, column);
569
570 // now solve R^T x = (A^T * column)
571 const int lda = this->current_size;
572 const int ldb = this->current_size;
573 const int N = this->current_size;
574 const int n_rhs = 1;
575 int info = 0;
577 'U', 'T', 'N', N, n_rhs, &this->R(0, 0), lda, &u(0), ldb, &info);
578
579 // finally get the diagonal element:
580 // rho2 = |column|^2 - |u|^2
581 const Number column_norm_sqr = column.norm_sqr();
582 const Number rho2 = column_norm_sqr - u.norm_sqr();
583 const bool linearly_independent =
584 column_signal.empty() ? rho2 > 0 :
585 column_signal(u, rho2, column_norm_sqr).get();
586
587 // bail out if it turns out to be linearly dependent
588 if (!linearly_independent)
589 return false;
590
591 // at this point we update is successful and we can enlarge R
592 // and store the column:
593 this->columns.push_back(std::make_unique<VectorType>(column));
594 this->R.grow_or_shrink(this->current_size + 1);
595 this->R(this->current_size, this->current_size) = std::sqrt(rho2);
596 for (unsigned int i = 0; i < this->current_size; ++i)
597 this->R(i, this->current_size) = u(i);
598
599 this->current_size++;
600 }
601
602 return true;
603}
604
605
606
607template <typename VectorType>
608void
610 const unsigned int k)
611{
612 AssertIndexRange(i, k);
613 AssertIndexRange(k, this->current_size);
614 const std::array<Number, 3> csr =
615 ::Utilities::LinearAlgebra::givens_rotation<Number>(this->R(i, k),
616 this->R(k, k));
617
618 // first, set k'th column:
619 this->R(i, k) = csr[2];
620 this->R(k, k) = 0.;
621 // now do the rest:
622 for (unsigned int j = 0; j < this->R.n(); ++j)
623 if (j != k)
624 {
625 const Number t = this->R(i, j);
626 this->R(i, j) = csr[0] * this->R(i, j) + csr[1] * this->R(k, j);
627 this->R(k, j) = -csr[1] * t + csr[0] * this->R(k, j);
628 }
629
630 if (!this->givens_signal.empty())
631 this->givens_signal(i, k, csr);
632}
633
634
635
636template <typename VectorType>
637void
638ImplicitQR<VectorType>::remove_column(const unsigned int k)
639{
640 // before actually removing a column from Q and resizing R,
641 // apply givens rotations to bring H into upper triangular form:
642 for (unsigned int j = k + 1; j < this->R.n(); ++j)
643 {
644 const unsigned int i = j - 1;
645 apply_givens_rotation(i, j);
646 }
647
648 // remove last row and k-th column
649 --this->current_size;
650 this->R.remove_row_and_column(this->current_size, k);
651
652 // Finally remove the column from A
653 this->columns.erase(this->columns.begin() + k);
654}
655
656
657
658template <typename VectorType>
659void
661 const Vector<Number> &x) const
662{
663 // A = QR
664 // A R^{-1} = Q
665 Vector<Number> x1 = x;
666 BaseQR<VectorType>::solve(x1, x1, false);
667 multiply_with_A(y, x1);
668}
669
670
671
672template <typename VectorType>
673void
675 const VectorType &x) const
676{
677 // A = QR
678 // A^T = R^T Q^T
679 // {R^T}^{-1} A^T = Q^T
680 multiply_with_AT(y, x);
681 BaseQR<VectorType>::solve(y, y, true);
682}
683
684
685
686template <typename VectorType>
687void
689 const Vector<Number> &x) const
690{
692}
693
694
695
696template <typename VectorType>
697void
699 const VectorType &x) const
700{
702}
703
704
705
706template <typename VectorType>
708 : BaseQR<VectorType>()
709{}
710
711
712
713template <typename VectorType>
714bool
715QR<VectorType>::append_column(const VectorType &column)
716{
717 // resize R:
718 this->R.grow_or_shrink(this->current_size + 1);
719 this->columns.push_back(std::make_unique<VectorType>(column));
720
721 // now a Gram-Schmidt part: orthonormalize the new column
722 // against everything we have so far:
723 auto &last_col = *this->columns.back();
724 for (unsigned int i = 0; i < this->current_size; ++i)
725 {
726 const auto &i_col = *this->columns[i];
727 this->R(i, this->current_size) = i_col * last_col;
728 last_col.add(-this->R(i, this->current_size), i_col);
729 }
730
731 this->R(this->current_size, this->current_size) = last_col.l2_norm();
732
733 Assert(this->R(this->current_size, this->current_size) > 0.,
735 last_col *= 1. / this->R(this->current_size, this->current_size);
736
737 ++this->current_size;
738 return true;
739}
740
741
742
743template <typename VectorType>
744void
745QR<VectorType>::apply_givens_rotation(const unsigned int i,
746 const unsigned int k)
747{
748 AssertIndexRange(i, k);
749 AssertIndexRange(k, this->current_size);
750 const std::array<Number, 3> csr =
751 ::Utilities::LinearAlgebra::givens_rotation<Number>(this->R(i, k),
752 this->R(k, k));
753
754 // first, set k'th column:
755 this->R(i, k) = csr[2];
756 this->R(k, k) = 0.;
757 // now do the rest:
758 for (unsigned int j = 0; j < this->R.n(); ++j)
759 if (j != k)
760 {
761 const Number t = this->R(i, j);
762 this->R(i, j) = csr[0] * this->R(i, j) + csr[1] * this->R(k, j);
763 this->R(k, j) = -csr[1] * t + csr[0] * this->R(k, j);
764 }
765
766 // now adjust i,k columns due to multiplication with the
767 // transpose Givens matrix from right:
768 auto &col_i = *this->columns[i];
769 auto &col_k = *this->columns[k];
770 // save column i:
771 tmp = col_i;
772 col_i.sadd(csr[0], csr[1], col_k);
773 col_k.sadd(csr[0], -csr[1], tmp);
774
775 if (!this->givens_signal.empty())
776 this->givens_signal(i, k, csr);
777}
778
779
780
781template <typename VectorType>
782void
783QR<VectorType>::remove_column(const unsigned int k)
784{
785 AssertIndexRange(k, this->current_size);
786 Assert(this->current_size > 0,
787 ExcMessage("Can not remove a column if QR is empty"));
788 // apply a sequence of Givens rotations
789 // see section 6.5 "Updating matrix factorizations" in Golub 2013, Matrix
790 // computations
791
792 // So we want to have QR for \tilde A \in R^{m*(n-1)}
793 // if we remove the column k, we end up with upper Hessenberg matrix
794 // x x x x x
795 // x x x x
796 // H = x x x
797 // x x x
798 // x x
799 // x
800 // where k = 2 (3rd column), m = 7, n = 6
801 //
802 // before actually removing a column from Q and resizing R,
803 // apply givens rotations to bring H into upper triangular form:
804 for (unsigned int j = k + 1; j < this->R.n(); ++j)
805 {
806 const unsigned int i = j - 1;
807 apply_givens_rotation(i, j);
808 }
809
810 // now we can throw away the column from Q and adjust R
811 // since we do thin-QR, after Givens rotations we need to throw
812 // away the last column:
813 const unsigned int size_minus_1 = this->columns.size() - 1;
814 this->columns.erase(this->columns.begin() + size_minus_1);
815
816 // remove last row and k-th column
817 --this->current_size;
818 this->R.remove_row_and_column(this->current_size, k);
819}
820
821
822
823template <typename VectorType>
824void
825QR<VectorType>::multiply_with_Q(VectorType &y, const Vector<Number> &x) const
826{
828}
829
830
831
832template <typename VectorType>
833void
834QR<VectorType>::multiply_with_QT(Vector<Number> &y, const VectorType &x) const
835{
837}
838
839
840
841template <typename VectorType>
842void
843QR<VectorType>::multiply_with_A(VectorType &y, const Vector<Number> &x) const
844{
845 Vector<Number> x1 = x;
846 const int N = this->current_size;
847 const int lda = N;
848 const int incx = 1;
850 'U', 'N', 'N', N, &this->R(0, 0), lda, &x1[0], incx);
851
852 multiply_with_Q(y, x1);
853}
854
855
856
857template <typename VectorType>
858void
859QR<VectorType>::multiply_with_AT(Vector<Number> &y, const VectorType &x) const
860{
861 multiply_with_QT(y, x);
862
863 const int N = this->current_size;
864 const int lda = N;
865 const int incx = 1;
867 'U', 'T', 'N', N, &this->R(0, 0), lda, &y[0], incx);
868}
869
870#endif // no DOXYGEN
871
873
874#endif
Definition qr.h:42
boost::signals2::connection connect_givens_slot(const std::function< void(const unsigned int i, const unsigned int j, const std::array< Number, 3 > &csr)> &slot)
void multiply_with_colsT(Vector< Number > &y, const VectorType &x) const
unsigned int size() const
virtual void remove_column(const unsigned int k=0)=0
virtual ~BaseQR()=default
virtual void multiply_with_Q(VectorType &y, const Vector< Number > &x) const =0
virtual void multiply_with_A(VectorType &y, const Vector< Number > &x) const =0
std::vector< std::unique_ptr< VectorType > > columns
Definition qr.h:157
virtual void multiply_with_AT(Vector< Number > &y, const VectorType &x) const =0
void solve(Vector< Number > &x, const Vector< Number > &y, const bool transpose=false) const
LAPACKFullMatrix< Number > R
Definition qr.h:162
typename VectorType::value_type Number
Definition qr.h:46
virtual bool append_column(const VectorType &column)=0
boost::signals2::signal< void(const unsigned int i, const unsigned int j, const std::array< Number, 3 > &)> givens_signal
Definition qr.h:176
void multiply_with_cols(VectorType &y, const Vector< Number > &x) const
unsigned int current_size
Definition qr.h:167
const LAPACKFullMatrix< Number > & get_R() const
virtual void multiply_with_QT(Vector< Number > &y, const VectorType &x) const =0
virtual ~ImplicitQR()=default
virtual void multiply_with_QT(Vector< Number > &y, const VectorType &x) const
virtual void multiply_with_A(VectorType &y, const Vector< Number > &x) const
boost::signals2::connection connect_append_column_slot(const std::function< bool(const Vector< Number > &u, const Number &rho2, const Number &col_norm_sqr)> &slot)
typename VectorType::value_type Number
Definition qr.h:314
boost::signals2::signal< bool(const Vector< Number > &u, const Number &rho, const Number &col_norm_sqr)> column_signal
Definition qr.h:389
virtual bool append_column(const VectorType &column)
virtual void multiply_with_Q(VectorType &y, const Vector< Number > &x) const
virtual void remove_column(const unsigned int k=0)
void apply_givens_rotation(const unsigned int i, const unsigned int k)
virtual void multiply_with_AT(Vector< Number > &y, const VectorType &x) const
void set_property(const LAPACKSupport::Property property)
Definition qr.h:200
virtual void multiply_with_Q(VectorType &y, const Vector< Number > &x) const override
typename VectorType::value_type Number
Definition qr.h:205
virtual void multiply_with_QT(Vector< Number > &y, const VectorType &x) const override
virtual void multiply_with_A(VectorType &y, const Vector< Number > &x) const override
virtual void multiply_with_AT(Vector< Number > &y, const VectorType &x) const override
void apply_givens_rotation(const unsigned int i, const unsigned int k)
virtual ~QR()=default
VectorType tmp
Definition qr.h:285
virtual void remove_column(const unsigned int k=0) override
QR()
virtual bool append_column(const VectorType &column) override
virtual size_type size() const override
real_type norm_sqr() const
#define DEAL_II_NAMESPACE_OPEN
Definition config.h:38
#define DEAL_II_NAMESPACE_CLOSE
Definition config.h:39
DerivativeForm< 1, spacedim, dim, Number > transpose(const DerivativeForm< 1, dim, spacedim, Number > &DF)
#define Assert(cond, exc)
#define AssertIndexRange(index, range)
static ::ExceptionBase & ExcDivideByZero()
static ::ExceptionBase & ExcDimensionMismatch(std::size_t arg1, std::size_t arg2)
static ::ExceptionBase & ExcMessage(std::string arg1)
@ upper_triangular
Matrix is upper triangular.
constexpr char N
Tpetra::Vector< Number, LO, GO, NodeType< MemorySpace > > VectorType
*  *  *  RotationFunction< dim, Number >::RotationFunction Number(dim)
void call_trmv(const char uplo, const char trans, const char diag, const types::blas_int n, const Number *a, const types::blas_int lda, Number *x, const types::blas_int incx)
Definition qr.cc:29
void call_trtrs(const char uplo, const char trans, const char diag, const types::blas_int n, const types::blas_int nrhs, const Number *a, const types::blas_int lda, Number *b, const types::blas_int ldb, types::blas_int *info)
Definition qr.cc:43
::VectorizedArray< Number, width > sqrt(const ::VectorizedArray< Number, width > &)