diff options
Diffstat (limited to 'src/armadillo/include/armadillo_bits/newarp_UpperHessenbergQR_meat.hpp')
-rw-r--r-- | src/armadillo/include/armadillo_bits/newarp_UpperHessenbergQR_meat.hpp | 310 |
1 files changed, 310 insertions, 0 deletions
diff --git a/src/armadillo/include/armadillo_bits/newarp_UpperHessenbergQR_meat.hpp b/src/armadillo/include/armadillo_bits/newarp_UpperHessenbergQR_meat.hpp new file mode 100644 index 0000000..c3a6fa8 --- /dev/null +++ b/src/armadillo/include/armadillo_bits/newarp_UpperHessenbergQR_meat.hpp @@ -0,0 +1,310 @@ +// SPDX-License-Identifier: Apache-2.0 +// +// Copyright 2008-2016 Conrad Sanderson (http://conradsanderson.id.au) +// Copyright 2008-2016 National ICT Australia (NICTA) +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// ------------------------------------------------------------------------ + + +namespace newarp +{ + + +template<typename eT> +inline +UpperHessenbergQR<eT>::UpperHessenbergQR() + : n(0) + , computed(false) + { + arma_extra_debug_sigprint(); + } + + + +template<typename eT> +inline +UpperHessenbergQR<eT>::UpperHessenbergQR(const Mat<eT>& mat_obj) + : n(mat_obj.n_rows) + , mat_T(n, n) + , rot_cos(n - 1) + , rot_sin(n - 1) + , computed(false) + { + arma_extra_debug_sigprint(); + + compute(mat_obj); + } + + + +template<typename eT> +void +UpperHessenbergQR<eT>::compute(const Mat<eT>& mat_obj) + { + arma_extra_debug_sigprint(); + + n = mat_obj.n_rows; + mat_T.set_size(n, n); + rot_cos.set_size(n - 1); + rot_sin.set_size(n - 1); + + // Make a copy of mat_obj + mat_T = mat_obj; + + eT xi, xj, r, c, s, eps = std::numeric_limits<eT>::epsilon(); + eT *ptr; + for(uword i = 0; i < n - 1; i++) + { + // Make sure mat_T is upper Hessenberg + // Zero the elements below mat_T(i + 1, i) + if(i < n - 2) { mat_T(span(i + 2, n - 1), i).zeros(); } + + xi = mat_T(i, i); // mat_T(i, i) + xj = mat_T(i + 1, i); // mat_T(i + 1, i) + r = arma_hypot(xi, xj); + if(r <= eps) + { + r = 0; + rot_cos(i) = c = 1; + rot_sin(i) = s = 0; + } + else + { + rot_cos(i) = c = xi / r; + rot_sin(i) = s = -xj / r; + } + + // For a complete QR decomposition, + // we first obtain the rotation matrix + // G = [ cos sin] + // [-sin cos] + // and then do T[i:(i + 1), i:(n - 1)] = G' * T[i:(i + 1), i:(n - 1)] + + // mat_T.submat(i, i, i + 1, n - 1) = Gt * mat_T.submat(i, i, i + 1, n - 1); + mat_T(i, i) = r; // mat_T(i, i) => r + mat_T(i + 1, i) = 0; // mat_T(i + 1, i) => 0 + ptr = &mat_T(i, i + 1); // mat_T(i, k), k = i+1, i+2, ..., n-1 + for(uword j = i + 1; j < n; j++, ptr += n) + { + eT tmp = ptr[0]; + ptr[0] = c * tmp - s * ptr[1]; + ptr[1] = s * tmp + c * ptr[1]; + } + } + + computed = true; + } + + + +template<typename eT> +Mat<eT> +UpperHessenbergQR<eT>::matrix_RQ() + { + arma_extra_debug_sigprint(); + + arma_debug_check( (computed == false), "newarp::UpperHessenbergQR::matrix_RQ(): need to call compute() first" ); + + // Make a copy of the R matrix + Mat<eT> RQ = trimatu(mat_T); + + for(uword i = 0; i < n - 1; i++) + { + // RQ[, i:(i + 1)] = RQ[, i:(i + 1)] * Gi + // Gi = [ cos[i] sin[i]] + // [-sin[i] cos[i]] + const eT c = rot_cos(i); + const eT s = rot_sin(i); + eT *Yi, *Yi1; + Yi = RQ.colptr(i); + Yi1 = RQ.colptr(i + 1); + for(uword j = 0; j < i + 2; j++) + { + eT tmp = Yi[j]; + Yi[j] = c * tmp - s * Yi1[j]; + Yi1[j] = s * tmp + c * Yi1[j]; + } + + /* Yi = RQ(span(0, i + 1), i); + RQ(span(0, i + 1), i) = (*c) * Yi - (*s) * RQ(span(0, i + 1), i + 1); + RQ(span(0, i + 1), i + 1) = (*s) * Yi + (*c) * RQ(span(0, i + 1), i + 1); */ + } + + return RQ; + } + + + +template<typename eT> +inline +void +UpperHessenbergQR<eT>::apply_YQ(Mat<eT>& Y) + { + arma_extra_debug_sigprint(); + + arma_debug_check( (computed == false), "newarp::UpperHessenbergQR::apply_YQ(): need to call compute() first" ); + + eT *Y_col_i, *Y_col_i1; + uword nrow = Y.n_rows; + for(uword i = 0; i < n - 1; i++) + { + const eT c = rot_cos(i); + const eT s = rot_sin(i); + Y_col_i = Y.colptr(i); + Y_col_i1 = Y.colptr(i + 1); + for(uword j = 0; j < nrow; j++) + { + eT tmp = Y_col_i[j]; + Y_col_i[j] = c * tmp - s * Y_col_i1[j]; + Y_col_i1[j] = s * tmp + c * Y_col_i1[j]; + } + } + } + + + +template<typename eT> +inline +TridiagQR<eT>::TridiagQR() + : UpperHessenbergQR<eT>() + { + arma_extra_debug_sigprint(); + } + + + +template<typename eT> +inline +TridiagQR<eT>::TridiagQR(const Mat<eT>& mat_obj) + : UpperHessenbergQR<eT>() + { + arma_extra_debug_sigprint(); + + this->compute(mat_obj); + } + + + +template<typename eT> +inline +void +TridiagQR<eT>::compute(const Mat<eT>& mat_obj) + { + arma_extra_debug_sigprint(); + + this->n = mat_obj.n_rows; + this->mat_T.set_size(this->n, this->n); + this->rot_cos.set_size(this->n - 1); + this->rot_sin.set_size(this->n - 1); + + this->mat_T.zeros(); + this->mat_T.diag() = mat_obj.diag(); + this->mat_T.diag(1) = mat_obj.diag(-1); + this->mat_T.diag(-1) = mat_obj.diag(-1); + + eT xi, xj, r, c, s, tmp, eps = std::numeric_limits<eT>::epsilon(); + eT *ptr; // A number of pointers to avoid repeated address calculation + for(uword i = 0; i < this->n - 1; i++) + { + xi = this->mat_T(i, i); // mat_T(i, i) + xj = this->mat_T(i + 1, i); // mat_T(i + 1, i) + r = arma_hypot(xi, xj); + if(r <= eps) + { + r = 0; + this->rot_cos(i) = c = 1; + this->rot_sin(i) = s = 0; + } + else + { + this->rot_cos(i) = c = xi / r; + this->rot_sin(i) = s = -xj / r; + } + + // For a complete QR decomposition, + // we first obtain the rotation matrix + // G = [ cos sin] + // [-sin cos] + // and then do T[i:(i + 1), i:(i + 2)] = G' * T[i:(i + 1), i:(i + 2)] + + // Update T[i, i] and T[i + 1, i] + // The updated value of T[i, i] is known to be r + // The updated value of T[i + 1, i] is known to be 0 + this->mat_T(i, i) = r; + this->mat_T(i + 1, i) = 0; + // Update T[i, i + 1] and T[i + 1, i + 1] + // ptr[0] == T[i, i + 1] + // ptr[1] == T[i + 1, i + 1] + ptr = &(this->mat_T(i, i + 1)); + tmp = *ptr; + ptr[0] = c * tmp - s * ptr[1]; + ptr[1] = s * tmp + c * ptr[1]; + + if(i < this->n - 2) + { + // Update T[i, i + 2] and T[i + 1, i + 2] + // ptr[0] == T[i, i + 2] == 0 + // ptr[1] == T[i + 1, i + 2] + ptr = &(this->mat_T(i, i + 2)); + ptr[0] = -s * ptr[1]; + ptr[1] *= c; + } + } + + this->computed = true; + } + + + +template<typename eT> +Mat<eT> +TridiagQR<eT>::matrix_RQ() + { + arma_extra_debug_sigprint(); + + arma_debug_check( (this->computed == false), "newarp::TridiagQR::matrix_RQ(): need to call compute() first" ); + + // Make a copy of the R matrix + Mat<eT> RQ(this->n, this->n, arma_zeros_indicator()); + RQ.diag() = this->mat_T.diag(); + RQ.diag(1) = this->mat_T.diag(1); + + // [m11 m12] will point to RQ[i:(i+1), i:(i+1)] + // [m21 m22] + eT *m11 = RQ.memptr(), *m12, *m21, *m22, tmp; + for(uword i = 0; i < this->n - 1; i++) + { + const eT c = this->rot_cos(i); + const eT s = this->rot_sin(i); + m21 = m11 + 1; + m12 = m11 + this->n; + m22 = m12 + 1; + tmp = *m21; + + // Update diagonal and the below-subdiagonal + *m11 = c * (*m11) - s * (*m12); + *m21 = c * tmp - s * (*m22); + *m22 = s * tmp + c * (*m22); + + // Move m11 to RQ[i+1, i+1] + m11 = m22; + } + + // Copy the below-subdiagonal to above-subdiagonal + RQ.diag(1) = RQ.diag(-1); + + return RQ; + } + + +} // namespace newarp |