Hi Patrick, I don't want to insist on getting my patches introduced. But as I have, meanwhile, learned a bit more about git and such, I thought I would make my patch more easily accessible. I've merged my changes onto the current git commit (as of today -- bb7f532c) and attach two clean patches (one for the Householder fix and one for the complex QR) that could be applied.
Cheers, Christian On 11/27/17 10:34 PM, Patrick Alken wrote: > Hi Christian, > > This should be very useful. I will take a look at your code as soon > as I can, though it may be a while as I am very busy with other things > at the moment. I will add this to the bug tracker so it isn't lost. > > Thanks, > Patrick > > On 11/27/2017 08:34 AM, Christian Krueger wrote: >> Hi, >> >> I was looking for the QR decomposition for complex-valued matrices. As >> GSL does not provide this yet, I have copied the code for the >> real-valued QR and modified it to handle complex-valued matrices. >> >> As I've seen in the archive that this feature has been requested a few >> times already, I am happy to share it with everyone who wants to use it. >> My github repository can be found at: >> https://github.com/mangroveck/gsl-qr-complex-devel.git >> >> The file linalg/qrc.c is essentially a copy of the file linalg/qr.c and >> provides a bunch of gsl_linalg_complex_QR_* functions. I've had to add >> "_complex" to the function calls and sometimes take the conjugate of tau >> when calling the householder-functions but the whole process was fairly >> straight-forward and the decomposition works. >> >> * My repository also includes my patch for the >> gsl_linalg_complex_householder_hv() function to handle the N=1 case. >> (I've just reported the bug on the bug-list). >> * I've added various tests to the file linalg/test.c to test the new >> gsl_linalg_complex_QR_* functions (in the same style as the tests for >> the real-valued QR decomp). >> * I've also adjusted the header file linalg/gsl_linalg.h and the file >> Makefile.am >> >> The only exception is the function gsl_linalg_complex_QR_update() which >> I haven't adjusted yet as it requires Givens rotations and they don't >> take complex values yet (within GSL). All other functionality from qr.c >> is now available for complex values, too. >> >> If it helps, I'm happy for this code to be included in GSL. >> >> Christian >> >> > > > > >
From 30d95fcca76e0c350b85fd5ee0f5a6f33b435bc3 Mon Sep 17 00:00:00 2001 From: Christian J Krueger <chri.k...@gmail.com> Date: Wed, 3 Jun 2020 12:25:32 +0200 Subject: [PATCH 1/2] fix pathological case in householdercomplex.c The pathological case of 1 dimension in gsl_linalg_complex_householder_hv() is treated individually. --- linalg/householdercomplex.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/linalg/householdercomplex.c b/linalg/householdercomplex.c index 3953c481..0cb832ba 100644 --- a/linalg/householdercomplex.c +++ b/linalg/householdercomplex.c @@ -123,6 +123,19 @@ gsl_linalg_complex_householder_hv (gsl_complex tau, const gsl_vector_complex * v if (GSL_REAL(tau) == 0.0 && GSL_IMAG(tau) == 0.0) return GSL_SUCCESS; + /* treat the N=1 case separately */ + if (N == 1) + { + gsl_complex w0 = gsl_vector_complex_get(w, 0); + gsl_complex tz = gsl_complex_mul(tau, w0); + gsl_complex ntz = gsl_complex_negative(tz); + gsl_complex w0ntz = gsl_complex_add(w0, ntz); + + gsl_vector_complex_set(w, 0, w0ntz); + + return GSL_SUCCESS; + } + { /* compute z = v'w */ -- 2.11.0
From b329da7d951d0c63080b730d859830c203cda04e Mon Sep 17 00:00:00 2001 From: Christian J Krueger <chri.k...@gmail.com> Date: Wed, 3 Jun 2020 12:27:00 +0200 Subject: [PATCH 2/2] add complex QR decomposition * File qr.c copied into qrc.c and adjusted all functions to allow for complex values (except gsl_linalg_QR_update() as this also requires complex-valued Givens rotations) * Implement test cases for complex valued QR decomposition based on the tests for the real-valued version. --- linalg/Makefile.am | 2 +- linalg/gsl_linalg.h | 70 ++++++ linalg/qrc.c | 644 ++++++++++++++++++++++++++++++++++++++++++++++++++++ linalg/test.c | 351 ++++++++++++++++++++++++++++ 4 files changed, 1066 insertions(+), 1 deletion(-) create mode 100644 linalg/qrc.c diff --git a/linalg/Makefile.am b/linalg/Makefile.am index c7fc58ce..cdc630d9 100644 --- a/linalg/Makefile.am +++ b/linalg/Makefile.am @@ -4,7 +4,7 @@ pkginclude_HEADERS = gsl_linalg.h AM_CPPFLAGS = -I$(top_srcdir) -libgsllinalg_la_SOURCES = cod.c condest.c invtri.c invtri_complex.c multiply.c exponential.c tridiag.c tridiag.h lu.c luc.c hh.c ql.c qr.c qrpt.c qr_tr.c rqr.c lq.c ptlq.c svd.c householder.c householdercomplex.c hessenberg.c hesstri.c cholesky.c choleskyc.c mcholesky.c pcholesky.c cholesky_band.c ldlt.c ldlt_band.c symmtd.c hermtd.c bidiag.c balance.c balancemat.c inline.c trimult.c trimult_complex.c +libgsllinalg_la_SOURCES = cod.c condest.c invtri.c invtri_complex.c multiply.c exponential.c tridiag.c tridiag.h lu.c luc.c hh.c ql.c qr.c qrc.c qrpt.c qr_tr.c rqr.c lq.c ptlq.c svd.c householder.c householdercomplex.c hessenberg.c hesstri.c cholesky.c choleskyc.c mcholesky.c pcholesky.c cholesky_band.c ldlt.c ldlt_band.c symmtd.c hermtd.c bidiag.c balance.c balancemat.c inline.c trimult.c trimult_complex.c noinst_HEADERS = apply_givens.c cholesky_common.c recurse.h svdstep.c tridiag.h test_cholesky.c test_choleskyc.c test_cod.c test_common.c test_ldlt.c test_lu.c test_luc.c test_lq.c test_ql.c test_qr.c test_tri.c diff --git a/linalg/gsl_linalg.h b/linalg/gsl_linalg.h index 6267942d..4a1e0e45 100644 --- a/linalg/gsl_linalg.h +++ b/linalg/gsl_linalg.h @@ -298,6 +298,76 @@ int gsl_linalg_R_svx (const gsl_matrix * R, gsl_vector * x); int gsl_linalg_QR_rcond(const gsl_matrix * QR, double * rcond, gsl_vector * work); +/* complex QR decomposition */ + +int gsl_linalg_complex_QR_decomp (gsl_matrix_complex * A, + gsl_vector_complex * tau); + + +int gsl_linalg_complex_QR_solve (const gsl_matrix_complex * QR, + const gsl_vector_complex * tau, + const gsl_vector_complex * b, + gsl_vector_complex * x); + + +int gsl_linalg_complex_QR_svx (const gsl_matrix_complex * QR, + const gsl_vector_complex * tau, + gsl_vector_complex * x); + +int gsl_linalg_complex_QR_lssolve (const gsl_matrix_complex * QR, + const gsl_vector_complex * tau, + const gsl_vector_complex * b, + gsl_vector_complex * x, + gsl_vector_complex * residual); + +int gsl_linalg_complex_QR_QRsolve (gsl_matrix_complex * Q, + gsl_matrix_complex * R, + const gsl_vector_complex * b, + gsl_vector_complex * x); + +int gsl_linalg_complex_QR_Rsolve (const gsl_matrix_complex * QR, + const gsl_vector_complex * b, + gsl_vector_complex * x); + +int gsl_linalg_complex_QR_Rsvx (const gsl_matrix_complex * QR, + gsl_vector_complex * x); + +/* +int gsl_linalg_QR_update (gsl_matrix * Q, + gsl_matrix * R, + gsl_vector * w, + const gsl_vector * v); +*/ + +int gsl_linalg_complex_QR_QTvec (const gsl_matrix_complex * QR, + const gsl_vector_complex * tau, + gsl_vector_complex * v); + +int gsl_linalg_complex_QR_Qvec (const gsl_matrix_complex * QR, + const gsl_vector_complex * tau, + gsl_vector_complex * v); + +int gsl_linalg_complex_QR_QTmat (const gsl_matrix_complex * QR, + const gsl_vector_complex * tau, + gsl_matrix_complex * A); + +int gsl_linalg_complex_QR_matQ (const gsl_matrix_complex * QR, + const gsl_vector_complex * tau, + gsl_matrix_complex * A); + +int gsl_linalg_complex_QR_unpack (const gsl_matrix_complex * QR, + const gsl_vector_complex * tau, + gsl_matrix_complex * Q, + gsl_matrix_complex * R); + +int gsl_linalg_complex_R_solve (const gsl_matrix_complex * R, + const gsl_vector_complex * b, + gsl_vector_complex * x); + +int gsl_linalg_complex_R_svx (const gsl_matrix_complex * R, + gsl_vector_complex * x); + + /* Q R P^T decomposition */ int gsl_linalg_QRPT_decomp (gsl_matrix * A, diff --git a/linalg/qrc.c b/linalg/qrc.c new file mode 100644 index 00000000..2d2e2649 --- /dev/null +++ b/linalg/qrc.c @@ -0,0 +1,644 @@ +/* linalg/qrc.c + * + * Copyright (C) 1996, 1997, 1998, 1999, 2000, 2007 Gerard Jungman, Brian Gough + * Copyright (C) 2017 Christian Krueger + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/* Author: G. Jungman, modified by C. Krueger */ + +#include <config.h> +#include <stdlib.h> +#include <string.h> +#include <gsl/gsl_linalg.h> +#include <gsl/gsl_math.h> +#include <gsl/gsl_vector.h> +#include <gsl/gsl_matrix.h> +#include <gsl/gsl_blas.h> +#include <gsl/gsl_complex_math.h> + +#include "apply_givens.c" + +/* + * This code is essentially the same as in qr.c but adapted for complex + * valued matrices. Adapting the function gsl_linalg_QR_update() is + * a bit more involved and has not been done yet. + */ + +/* Factorise a general complex-valued M x N matrix A into + * + * A = Q R + * + * where Q is unitary (M x M) and R is upper triangular (M x N). + * + * Q is stored as a packed set of Householder transformations in the + * strict lower triangular part of the input matrix. + * + * R is stored in the diagonal and upper triangle of the input matrix. + * + * The full matrix for Q can be obtained as the product + * + * Q = Q_k .. Q_2 Q_1 + * + * where k = MIN(M,N) and + * + * Q_i = (I - tau_i * v_i * v_i') + * + * and where v_i is a Householder vector + * + * v_i = [1, m(i+1,i), m(i+2,i), ... , m(M,i)] + * + * This storage scheme is the same as in LAPACK. */ + +int +gsl_linalg_complex_QR_decomp (gsl_matrix_complex * A, gsl_vector_complex * tau) +{ + const size_t M = A->size1; + const size_t N = A->size2; + + if (tau->size != GSL_MIN (M, N)) + { + GSL_ERROR ("size of tau must be MIN(M,N)", GSL_EBADLEN); + } + else + { + size_t i; + + for (i = 0; i < GSL_MIN (M, N); i++) + { + /* Compute the Householder transformation to reduce the j-th + column of the matrix to a multiple of the j-th unit vector */ + + gsl_vector_complex_view c_full = gsl_matrix_complex_column (A, i); + gsl_vector_complex_view c = gsl_vector_complex_subvector (&(c_full.vector), i, M-i); + + gsl_complex tau_i = gsl_linalg_complex_householder_transform (&(c.vector)); + + gsl_vector_complex_set (tau, i, tau_i); + + /* Apply the transformation to the remaining columns and + update the norms */ + + if (i + 1 < N) + { + gsl_matrix_complex_view m = gsl_matrix_complex_submatrix (A, i, i + 1, M - i, N - (i + 1)); + gsl_complex tau_i_conj = gsl_complex_conjugate(tau_i); + gsl_linalg_complex_householder_hm (tau_i_conj, &(c.vector), &(m.matrix)); + } + } + + return GSL_SUCCESS; + } +} + +/* Solves the system A x = b using the QR factorisation, + + * R x = Q^* b + * + * to obtain x. Based on SLATEC code. + */ + +int +gsl_linalg_complex_QR_solve (const gsl_matrix_complex * QR, const gsl_vector_complex * tau, const gsl_vector_complex * b, gsl_vector_complex * x) +{ + if (QR->size1 != QR->size2) + { + GSL_ERROR ("QR matrix must be square", GSL_ENOTSQR); + } + else if (QR->size1 != b->size) + { + GSL_ERROR ("matrix size must match b size", GSL_EBADLEN); + } + else if (QR->size2 != x->size) + { + GSL_ERROR ("matrix size must match solution size", GSL_EBADLEN); + } + else + { + /* Copy x <- b */ + + gsl_vector_complex_memcpy (x, b); + + /* Solve for x */ + + gsl_linalg_complex_QR_svx (QR, tau, x); + + return GSL_SUCCESS; + } +} + +/* Solves the system A x = b in place using the QR factorisation, + + * R x = Q^* b + * + * to obtain x. Based on SLATEC code. + */ + +int +gsl_linalg_complex_QR_svx (const gsl_matrix_complex * QR, const gsl_vector_complex * tau, gsl_vector_complex * x) +{ + + if (QR->size1 != QR->size2) + { + GSL_ERROR ("QR matrix must be square", GSL_ENOTSQR); + } + else if (QR->size1 != x->size) + { + GSL_ERROR ("matrix size must match x/rhs size", GSL_EBADLEN); + } + else + { + /* compute rhs = Q^* b */ + + gsl_linalg_complex_QR_QTvec (QR, tau, x); + + /* Solve R x = rhs, storing x in-place */ + + gsl_blas_ztrsv (CblasUpper, CblasNoTrans, CblasNonUnit, QR, x); + + return GSL_SUCCESS; + } +} + + +/* Find the least squares solution to the overdetermined system + * + * A x = b + * + * for M >= N using the QR factorization A = Q R. + */ + +int +gsl_linalg_complex_QR_lssolve (const gsl_matrix_complex * QR, const gsl_vector_complex * tau, const gsl_vector_complex * b, gsl_vector_complex * x, gsl_vector_complex * residual) +{ + const size_t M = QR->size1; + const size_t N = QR->size2; + + if (M < N) + { + GSL_ERROR ("QR matrix must have M>=N", GSL_EBADLEN); + } + else if (M != b->size) + { + GSL_ERROR ("matrix size must match b size", GSL_EBADLEN); + } + else if (N != x->size) + { + GSL_ERROR ("matrix size must match solution size", GSL_EBADLEN); + } + else if (M != residual->size) + { + GSL_ERROR ("matrix size must match residual size", GSL_EBADLEN); + } + else + { + gsl_matrix_complex_const_view R = gsl_matrix_complex_const_submatrix (QR, 0, 0, N, N); + gsl_vector_complex_view c = gsl_vector_complex_subvector(residual, 0, N); + + gsl_vector_complex_memcpy(residual, b); + + /* compute rhs = Q^* b */ + + gsl_linalg_complex_QR_QTvec (QR, tau, residual); + + /* Solve R x = rhs */ + + gsl_vector_complex_memcpy(x, &(c.vector)); + + gsl_blas_ztrsv (CblasUpper, CblasNoTrans, CblasNonUnit, &(R.matrix), x); + + /* Compute residual = b - A x = Q (Q^* b - R x) */ + + gsl_vector_complex_set_zero(&(c.vector)); + + gsl_linalg_complex_QR_Qvec(QR, tau, residual); + + return GSL_SUCCESS; + } +} + + +int +gsl_linalg_complex_QR_Rsolve (const gsl_matrix_complex * QR, const gsl_vector_complex * b, gsl_vector_complex * x) +{ + if (QR->size1 != QR->size2) + { + GSL_ERROR ("QR matrix must be square", GSL_ENOTSQR); + } + else if (QR->size1 != b->size) + { + GSL_ERROR ("matrix size must match b size", GSL_EBADLEN); + } + else if (QR->size2 != x->size) + { + GSL_ERROR ("matrix size must match x size", GSL_EBADLEN); + } + else + { + /* Copy x <- b */ + + gsl_vector_complex_memcpy (x, b); + + /* Solve R x = b, storing x in-place */ + + gsl_blas_ztrsv (CblasUpper, CblasNoTrans, CblasNonUnit, QR, x); + + return GSL_SUCCESS; + } +} + + +int +gsl_linalg_complex_QR_Rsvx (const gsl_matrix_complex * QR, gsl_vector_complex * x) +{ + if (QR->size1 != QR->size2) + { + GSL_ERROR ("QR matrix must be square", GSL_ENOTSQR); + } + else if (QR->size1 != x->size) + { + GSL_ERROR ("matrix size must match rhs size", GSL_EBADLEN); + } + else + { + /* Solve R x = b, storing x in-place */ + + gsl_blas_ztrsv (CblasUpper, CblasNoTrans, CblasNonUnit, QR, x); + + return GSL_SUCCESS; + } +} + +int +gsl_linalg_complex_R_solve (const gsl_matrix_complex * R, const gsl_vector_complex * b, gsl_vector_complex * x) +{ + if (R->size1 != R->size2) + { + GSL_ERROR ("R matrix must be square", GSL_ENOTSQR); + } + else if (R->size1 != b->size) + { + GSL_ERROR ("matrix size must match b size", GSL_EBADLEN); + } + else if (R->size2 != x->size) + { + GSL_ERROR ("matrix size must match solution size", GSL_EBADLEN); + } + else + { + /* Copy x <- b */ + + gsl_vector_complex_memcpy (x, b); + + /* Solve R x = b, storing x inplace in b */ + + gsl_blas_ztrsv (CblasUpper, CblasNoTrans, CblasNonUnit, R, x); + + return GSL_SUCCESS; + } +} + +int +gsl_linalg_complex_R_svx (const gsl_matrix_complex * R, gsl_vector_complex * x) +{ + if (R->size1 != R->size2) + { + GSL_ERROR ("R matrix must be square", GSL_ENOTSQR); + } + else if (R->size2 != x->size) + { + GSL_ERROR ("matrix size must match solution size", GSL_EBADLEN); + } + else + { + /* Solve R x = b, storing x inplace in b */ + + gsl_blas_ztrsv (CblasUpper, CblasNoTrans, CblasNonUnit, R, x); + + return GSL_SUCCESS; + } +} + + +/* Form the product Q^* v from a QR factorized matrix + */ + +int +gsl_linalg_complex_QR_QTvec (const gsl_matrix_complex * QR, const gsl_vector_complex * tau, gsl_vector_complex * v) +{ + const size_t M = QR->size1; + const size_t N = QR->size2; + + if (tau->size != GSL_MIN (M, N)) + { + GSL_ERROR ("size of tau must be MIN(M,N)", GSL_EBADLEN); + } + else if (v->size != M) + { + GSL_ERROR ("vector size must be M", GSL_EBADLEN); + } + else + { + size_t i; + + /* compute Q^* v */ + + for (i = 0; i < GSL_MIN (M, N); i++) + { + gsl_vector_complex_const_view c = gsl_matrix_complex_const_column (QR, i); + gsl_vector_complex_const_view h = gsl_vector_complex_const_subvector (&(c.vector), i, M - i); + gsl_vector_complex_view w = gsl_vector_complex_subvector (v, i, M - i); + gsl_complex ti = gsl_vector_complex_get (tau, i); + gsl_complex ti_conj = gsl_complex_conjugate(ti); + gsl_linalg_complex_householder_hv (ti_conj, &(h.vector), &(w.vector)); + } + return GSL_SUCCESS; + } +} + + +int +gsl_linalg_complex_QR_Qvec (const gsl_matrix_complex * QR, const gsl_vector_complex * tau, gsl_vector_complex * v) +{ + const size_t M = QR->size1; + const size_t N = QR->size2; + + if (tau->size != GSL_MIN (M, N)) + { + GSL_ERROR ("size of tau must be MIN(M,N)", GSL_EBADLEN); + } + else if (v->size != M) + { + GSL_ERROR ("vector size must be M", GSL_EBADLEN); + } + else + { + size_t i; + + /* compute Q v */ + + for (i = GSL_MIN (M, N); i-- > 0;) + { + gsl_vector_complex_const_view c = gsl_matrix_complex_const_column (QR, i); + gsl_vector_complex_const_view h = gsl_vector_complex_const_subvector (&(c.vector), + i, M - i); + gsl_vector_complex_view w = gsl_vector_complex_subvector (v, i, M - i); + gsl_complex ti = gsl_vector_complex_get (tau, i); + /* we do not need the conjugate of ti here */ + gsl_linalg_complex_householder_hv (ti, &h.vector, &w.vector); + } + return GSL_SUCCESS; + } +} + +/* Form the product Q^* A from a QR factorized matrix */ + +int +gsl_linalg_complex_QR_QTmat (const gsl_matrix_complex * QR, const gsl_vector_complex * tau, gsl_matrix_complex * A) +{ + const size_t M = QR->size1; + const size_t N = QR->size2; + + if (tau->size != GSL_MIN (M, N)) + { + GSL_ERROR ("size of tau must be MIN(M,N)", GSL_EBADLEN); + } + else if (A->size1 != M) + { + GSL_ERROR ("matrix must have M rows", GSL_EBADLEN); + } + else + { + size_t i; + + /* compute Q^* A */ + + for (i = 0; i < GSL_MIN (M, N); i++) + { + gsl_vector_complex_const_view c = gsl_matrix_complex_const_column (QR, i); + gsl_vector_complex_const_view h = gsl_vector_complex_const_subvector (&(c.vector), i, M - i); + gsl_matrix_complex_view m = gsl_matrix_complex_submatrix(A, i, 0, M - i, A->size2); + gsl_complex ti = gsl_vector_complex_get (tau, i); + gsl_complex ti_conj = gsl_complex_conjugate(ti); + gsl_linalg_complex_householder_hm (ti_conj, &(h.vector), &(m.matrix)); + } + return GSL_SUCCESS; + } +} + +/* Form the product A Q from a QR factorized matrix */ +int +gsl_linalg_complex_QR_matQ (const gsl_matrix_complex * QR, const gsl_vector_complex * tau, gsl_matrix_complex * A) +{ + const size_t M = QR->size1; + const size_t N = QR->size2; + + if (tau->size != GSL_MIN (M, N)) + { + GSL_ERROR ("size of tau must be MIN(M,N)", GSL_EBADLEN); + } + else if (A->size2 != M) + { + GSL_ERROR ("matrix must have M columns", GSL_EBADLEN); + } + else + { + size_t i; + + /* compute A Q */ + + for (i = 0; i < GSL_MIN (M, N); i++) + { + gsl_vector_complex_const_view c = gsl_matrix_complex_const_column (QR, i); + gsl_vector_complex_const_view h = gsl_vector_complex_const_subvector (&(c.vector), i, M - i); + gsl_matrix_complex_view m = gsl_matrix_complex_submatrix(A, 0, i, A->size1, M - i); + gsl_complex ti = gsl_vector_complex_get (tau, i); + /* we do not need the conjugate of ti here */ + gsl_linalg_complex_householder_mh (ti, &(h.vector), &(m.matrix)); + } + return GSL_SUCCESS; + } +} + +/* Form the unitary matrix Q from the packed QR matrix */ + +int +gsl_linalg_complex_QR_unpack (const gsl_matrix_complex * QR, const gsl_vector_complex * tau, gsl_matrix_complex * Q, gsl_matrix_complex * R) +{ + const size_t M = QR->size1; + const size_t N = QR->size2; + + if (Q->size1 != M || Q->size2 != M) + { + GSL_ERROR ("Q matrix must be M x M", GSL_ENOTSQR); + } + else if (R->size1 != M || R->size2 != N) + { + GSL_ERROR ("R matrix must be M x N", GSL_ENOTSQR); + } + else if (tau->size != GSL_MIN (M, N)) + { + GSL_ERROR ("size of tau must be MIN(M,N)", GSL_EBADLEN); + } + else + { + size_t i, j; + + /* Initialize Q to the identity */ + + gsl_matrix_complex_set_identity (Q); + + for (i = GSL_MIN (M, N); i-- > 0;) + { + gsl_vector_complex_const_view c = gsl_matrix_complex_const_column (QR, i); + gsl_vector_complex_const_view h = gsl_vector_complex_const_subvector (&c.vector, i, M - i); + gsl_matrix_complex_view m = gsl_matrix_complex_submatrix (Q, i, i, M - i, M - i); + gsl_complex ti = gsl_vector_complex_get (tau, i); + /* we do not need the conjugate of ti here */ + gsl_linalg_complex_householder_hm (ti, &h.vector, &m.matrix); + } + + /* Form the right triangular matrix R from a packed QR matrix */ + + for (i = 0; i < M; i++) + { + for (j = 0; j < i && j < N; j++) + gsl_matrix_complex_set (R, i, j, GSL_COMPLEX_ZERO); + + for (j = i; j < N; j++) + gsl_matrix_complex_set (R, i, j, gsl_matrix_complex_get (QR, i, j)); + } + + return GSL_SUCCESS; + } +} +// +// +// TODO: This function still needs adapting to complex numbers. +// However, this also requires the functions supplied by GSL +// for the Givens rotations (gsl_linalg_givens_*) to be adapted. +// +// /* Update a QR factorisation for A= Q R , A' = A + u v^T, +// +// * Q' R' = QR + u v^T +// * = Q (R + Q^T u v^T) +// * = Q (R + w v^T) +// * +// * where w = Q^T u. +// * +// * Algorithm from Golub and Van Loan, "Matrix Computations", Section +// * 12.5 (Updating Matrix Factorizations, Rank-One Changes) +// */ +// +// int +// gsl_linalg_QR_update (gsl_matrix * Q, gsl_matrix * R, +// gsl_vector * w, const gsl_vector * v) +// { +// const size_t M = R->size1; +// const size_t N = R->size2; +// +// if (Q->size1 != M || Q->size2 != M) +// { +// GSL_ERROR ("Q matrix must be M x M if R is M x N", GSL_ENOTSQR); +// } +// else if (w->size != M) +// { +// GSL_ERROR ("w must be length M if R is M x N", GSL_EBADLEN); +// } +// else if (v->size != N) +// { +// GSL_ERROR ("v must be length N if R is M x N", GSL_EBADLEN); +// } +// else +// { +// size_t j, k; +// double w0; +// +// /* Apply Given's rotations to reduce w to (|w|, 0, 0, ... , 0) +// +// J_1^T .... J_(n-1)^T w = +/- |w| e_1 +// +// simultaneously applied to R, H = J_1^T ... J^T_(n-1) R +// so that H is upper Hessenberg. (12.5.2) */ +// +// for (k = M - 1; k > 0; k--) /* loop from k = M-1 to 1 */ +// { +// double c, s; +// double wk = gsl_vector_get (w, k); +// double wkm1 = gsl_vector_get (w, k - 1); +// +// gsl_linalg_givens (wkm1, wk, &c, &s); +// gsl_linalg_givens_gv (w, k - 1, k, c, s); +// apply_givens_qr (M, N, Q, R, k - 1, k, c, s); +// } +// +// w0 = gsl_vector_get (w, 0); +// +// /* Add in w v^T (Equation 12.5.3) */ +// +// for (j = 0; j < N; j++) +// { +// double r0j = gsl_matrix_get (R, 0, j); +// double vj = gsl_vector_get (v, j); +// gsl_matrix_set (R, 0, j, r0j + w0 * vj); +// } +// +// /* Apply Givens transformations R' = G_(n-1)^T ... G_1^T H +// Equation 12.5.4 */ +// +// for (k = 1; k < GSL_MIN(M,N+1); k++) +// { +// double c, s; +// double diag = gsl_matrix_get (R, k - 1, k - 1); +// double offdiag = gsl_matrix_get (R, k, k - 1); +// +// gsl_linalg_givens (diag, offdiag, &c, &s); +// apply_givens_qr (M, N, Q, R, k - 1, k, c, s); +// +// gsl_matrix_set (R, k, k - 1, 0.0); /* exact zero of G^T */ +// } +// +// return GSL_SUCCESS; +// } +// } + +int +gsl_linalg_complex_QR_QRsolve (gsl_matrix_complex * Q, gsl_matrix_complex * R, const gsl_vector_complex * b, gsl_vector_complex * x) +{ + const size_t M = R->size1; + const size_t N = R->size2; + + if (M != N) + { + return GSL_ENOTSQR; + } + else if (Q->size1 != M || b->size != M || x->size != M) + { + return GSL_EBADLEN; + } + else + { + /* compute sol = Q^* b */ + + gsl_blas_zgemv (CblasConjTrans, GSL_COMPLEX_ONE, Q, b, GSL_COMPLEX_ZERO, x); + + /* Solve R x = sol, storing x in-place */ + + gsl_blas_ztrsv (CblasUpper, CblasNoTrans, CblasNonUnit, R, x); + + return GSL_SUCCESS; + } +} diff --git a/linalg/test.c b/linalg/test.c index a5273085..e30b9f8c 100644 --- a/linalg/test.c +++ b/linalg/test.c @@ -2,6 +2,7 @@ * * Copyright (C) 1996, 1997, 1998, 1999, 2000, 2004, 2005, 2006, 2007, 2010 Gerard Jungman, Brian Gough * Copyright (C) Huan Wu (test_choleskyc_invert and test_choleskyc_invert_dim) + * Copyright (C) 2017 Christian Krueger (test_QRc_*) * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -158,6 +159,19 @@ create_general_matrix(unsigned long size1, unsigned long size2) return m; } +gsl_matrix_complex * +create_general_matrix_complex(unsigned long size1, unsigned long size2) +{ + unsigned long i, j; + gsl_matrix_complex * m = gsl_matrix_complex_alloc(size1, size2); + for(i=0; i<size1; i++) { + for(j=0; j<size2; j++) { + gsl_matrix_complex_set(m, i, j, gsl_complex_rect(1.0/(i+j+1.0),1.0/(1+size1+size2-i-j))); + } + } + return m; +} + gsl_matrix * create_singular_matrix(unsigned long size1, unsigned long size2) { @@ -190,6 +204,20 @@ create_vandermonde_matrix(unsigned long size) return m; } +gsl_matrix_complex * +create_vandermonde_matrix_complex(unsigned long size) +{ + unsigned long i, j; + gsl_matrix_complex * m = gsl_matrix_complex_alloc(size, size); + for(i=0; i<size; i++) { + for(j=0; j<size; j++) { + gsl_complex a = gsl_complex_rect(i + 1.0, i*0.5); + gsl_matrix_complex_set(m, i, j, gsl_complex_pow_real(a, size - j - 1.0)); + } + } + return m; +} + gsl_matrix * create_moler_matrix(unsigned long size) { @@ -330,6 +358,10 @@ gsl_matrix * m35; gsl_matrix * m53; gsl_matrix * m97; +gsl_matrix_complex * m35c; +gsl_matrix_complex * m53c; +gsl_matrix_complex * m97c; + gsl_matrix * s35; gsl_matrix * s53; @@ -356,6 +388,10 @@ gsl_matrix * bigsparse; double m53_lssolution[] = {52.5992295702070, -337.7263113752073, 351.8823436427604}; +double m53c_lssolution[] = {-2.42665224693892, -6.55069697384445, + -5.47280378950620, 35.11460791136691, + 13.84982610972922,-36.12898835153614}; + double hilb2_solution[] = {-8.0, 18.0} ; double hilb3_solution[] = {27.0, -192.0, 210.0}; double hilb4_solution[] = {-64.0, 900.0, -2520.0, 1820.0}; @@ -384,6 +420,18 @@ double vander12_solution[] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0}; +gsl_matrix_complex * vander2c; +gsl_matrix_complex * vander3c; +gsl_matrix_complex * vander4c; +gsl_matrix_complex * vander12c; + +double vander2c_solution[] = {1.0, 0.0, 0.0, 0.0}; +double vander3c_solution[] = {0.0, 0.0, 1.0, 0.0, 0.0, 0.0}; +double vander4c_solution[] = {0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0}; +double vander12c_solution[] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, + 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0}; + gsl_matrix * moler10; #include "test_common.c" @@ -643,6 +691,296 @@ int test_QR_lssolve(void) } int +test_QRc_solve_dim(const gsl_matrix_complex * m, const double * actual, double eps) +{ + int s = 0; + unsigned long i, dim = m->size1; + + gsl_vector_complex * rhs = gsl_vector_complex_alloc(dim); + gsl_matrix_complex * qr = gsl_matrix_complex_alloc(dim,dim); + gsl_vector_complex * d = gsl_vector_complex_alloc(dim); + gsl_vector_complex * x = gsl_vector_complex_alloc(dim); + + gsl_matrix_complex_memcpy(qr,m); + for(i=0; i<dim; i++) { + gsl_complex a = gsl_complex_rect(i+1.0, 0.5*i); + gsl_vector_complex_set(rhs, i, a); + } + s += gsl_linalg_complex_QR_decomp(qr, d); + s += gsl_linalg_complex_QR_solve(qr, d, rhs, x); + for(i=0; i<dim; i++) { + int foo_r = check(GSL_REAL(gsl_vector_complex_get(x, i)), actual[2*i], eps); + int foo_i = check(GSL_IMAG(gsl_vector_complex_get(x, i)), actual[2*i+1], eps); + if(foo_r || foo_i) { + printf("%3lu[%lu]: %22.18g %22.18g\n", dim, i, GSL_REAL(gsl_vector_complex_get(x, i)), actual[2*i]); + printf("%3lu[%lu]: %22.18g %22.18g\n", dim, i, GSL_IMAG(gsl_vector_complex_get(x, i)), actual[2*i+1]); + } + s += foo_r + foo_i; + } + + gsl_vector_complex_free(x); + gsl_vector_complex_free(d); + gsl_matrix_complex_free(qr); + gsl_vector_complex_free(rhs); + + return s; +} + +int test_QRc_solve(void) +{ + int f; + int s = 0; + + f = test_QRc_solve_dim(vander2c, vander2c_solution, 16.0 * GSL_DBL_EPSILON); + gsl_test(f, " QRc_solve vander(2)"); + s += f; + + f = test_QRc_solve_dim(vander3c, vander3c_solution, 64.0 * GSL_DBL_EPSILON); + gsl_test(f, " QRc_solve vander(3)"); + s += f; + + f = test_QRc_solve_dim(vander4c, vander4c_solution, 1024.0 * GSL_DBL_EPSILON); + gsl_test(f, " QRc_solve vander(4)"); + s += f; + + f = test_QRc_solve_dim(vander12c, vander12c_solution, 0.05); + gsl_test(f, " QRc_solve vander(12)"); + s += f; + + return s; +} + +int +test_QRc_QRsolve_dim(const gsl_matrix_complex * m, const double * actual, double eps) +{ + int s = 0; + unsigned long i, dim = m->size1; + + gsl_vector_complex * rhs = gsl_vector_complex_alloc(dim); + gsl_matrix_complex * qr = gsl_matrix_complex_alloc(dim,dim); + gsl_matrix_complex * q = gsl_matrix_complex_alloc(dim,dim); + gsl_matrix_complex * r = gsl_matrix_complex_alloc(dim,dim); + gsl_vector_complex * d = gsl_vector_complex_alloc(dim); + gsl_vector_complex * x = gsl_vector_complex_alloc(dim); + + gsl_matrix_complex_memcpy(qr,m); + for(i=0; i<dim; i++) { + gsl_complex a = gsl_complex_rect(i+1.0, 0.5*i); + gsl_vector_complex_set(rhs, i, a); + } + s += gsl_linalg_complex_QR_decomp(qr, d); + s += gsl_linalg_complex_QR_unpack(qr, d, q, r); + s += gsl_linalg_complex_QR_QRsolve(q, r, rhs, x); + for(i=0; i<dim; i++) { + int foo_r = check(GSL_REAL(gsl_vector_complex_get(x, i)), actual[2*i], eps); + int foo_i = check(GSL_IMAG(gsl_vector_complex_get(x, i)), actual[2*i+1], eps); + if(foo_r || foo_i) { + printf("%3lu[%lu]: %22.18g %22.18g\n", dim, i, GSL_REAL(gsl_vector_complex_get(x, i)), actual[2*i]); + printf("%3lu[%lu]: %22.18g %22.18g\n", dim, i, GSL_IMAG(gsl_vector_complex_get(x, i)), actual[2*i+1]); + } + s += foo_r || foo_i; + } + + gsl_vector_complex_free(x); + gsl_vector_complex_free(d); + gsl_matrix_complex_free(qr); + gsl_matrix_complex_free(q); + gsl_matrix_complex_free(r); + gsl_vector_complex_free(rhs); + return s; +} + +int test_QRc_QRsolve(void) +{ + int f; + int s = 0; + + f = test_QRc_QRsolve_dim(vander2c, vander2c_solution, 8.0 * GSL_DBL_EPSILON); + gsl_test(f, " QRc_QRsolve vander(2)"); + s += f; + + f = test_QRc_QRsolve_dim(vander3c, vander3c_solution, 64.0 * GSL_DBL_EPSILON); + gsl_test(f, " QRc_QRsolve vander(3)"); + s += f; + + f = test_QRc_QRsolve_dim(vander4c, vander4c_solution, 1024.0 * GSL_DBL_EPSILON); + gsl_test(f, " QRc_QRsolve vander(4)"); + s += f; + + f = test_QRc_QRsolve_dim(vander12c, vander12c_solution, 0.05); + gsl_test(f, " QRc_QRsolve vander(12)"); + s += f; + + return s; +} + +int +test_QRc_lssolve_dim(const gsl_matrix_complex * m, const double * actual, double eps) +{ + int s = 0; + unsigned long i, M = m->size1, N = m->size2; + + gsl_vector_complex * rhs = gsl_vector_complex_alloc(M); + gsl_matrix_complex * qr = gsl_matrix_complex_alloc(M,N); + gsl_vector_complex * d = gsl_vector_complex_alloc(N); + gsl_vector_complex * x = gsl_vector_complex_alloc(N); + gsl_vector_complex * r = gsl_vector_complex_alloc(M); + gsl_vector_complex * res = gsl_vector_complex_alloc(M); + + gsl_matrix_complex_memcpy(qr,m); + for(i=0; i<M; i++) { + gsl_complex a = gsl_complex_rect(i+1.0, 0.5*i); + gsl_vector_complex_set(rhs, i, a); + } + s += gsl_linalg_complex_QR_decomp(qr, d); + s += gsl_linalg_complex_QR_lssolve(qr, d, rhs, x, res); + + for(i=0; i<N; i++) { + int foo_r = check(GSL_REAL(gsl_vector_complex_get(x, i)), actual[2*i], eps); + int foo_i = check(GSL_IMAG(gsl_vector_complex_get(x, i)), actual[2*i+1], eps); + if(foo_r || foo_i) { + printf("(%3lu,%3lu)[%lu]: %22.18g %22.18g\n", M, N, i, GSL_REAL(gsl_vector_complex_get(x, i)), actual[2*i]); + printf("(%3lu,%3lu)[%lu]: %22.18g %22.18g\n", M, N, i, GSL_IMAG(gsl_vector_complex_get(x, i)), actual[2*i+1]); + } + s += foo_r + foo_i; + } + + /* compute residual r = b - m x */ + if (M == N) { + gsl_vector_complex_set_zero(r); + } else { + gsl_vector_complex_memcpy(r, rhs); + gsl_blas_zgemv(CblasNoTrans, gsl_complex_rect(-1.0, 0.0), m, x, GSL_COMPLEX_ONE, r); + }; + + for(i=0; i<N; i++) { + int foo_r = check(GSL_REAL(gsl_vector_complex_get(res, i)), GSL_REAL(gsl_vector_complex_get(r,i)), sqrt(eps)); + int foo_i = check(GSL_IMAG(gsl_vector_complex_get(res, i)), GSL_IMAG(gsl_vector_complex_get(r,i)), sqrt(eps)); + if(foo_r || foo_i) { + printf("(%3lu,%3lu)[%lu]: %22.18g %22.18g\n", M, N, i, GSL_REAL(gsl_vector_complex_get(res, i)), GSL_REAL(gsl_vector_complex_get(r,i))); + printf("(%3lu,%3lu)[%lu]: %22.18g %22.18g\n", M, N, i, GSL_IMAG(gsl_vector_complex_get(res, i)), GSL_IMAG(gsl_vector_complex_get(r,i))); + } + s += foo_r + foo_i; + } + + gsl_vector_complex_free(r); + gsl_vector_complex_free(res); + gsl_vector_complex_free(x); + gsl_vector_complex_free(d); + gsl_matrix_complex_free(qr); + gsl_vector_complex_free(rhs); + + return s; +} + +int test_QRc_lssolve(void) +{ + int f; + int s = 0; + + f = test_QRc_lssolve_dim(m53c, m53c_lssolution, 2 * 64.0 * GSL_DBL_EPSILON); + gsl_test(f, " QRc_lssolve m(5,3)"); + s += f; + + f = test_QRc_lssolve_dim(vander2c, vander2c_solution, 16.0 * GSL_DBL_EPSILON); + gsl_test(f, " QRc_lssolve vander(2)"); + s += f; + + f = test_QRc_lssolve_dim(vander3c, vander3c_solution, 64.0 * GSL_DBL_EPSILON); + gsl_test(f, " QRc_lssolve vander(3)"); + s += f; + + f = test_QRc_lssolve_dim(vander4c, vander4c_solution, 1024.0 * GSL_DBL_EPSILON); + gsl_test(f, " QRc_lssolve vander(4)"); + s += f; + + f = test_QRc_lssolve_dim(vander12c, vander12c_solution, 0.05); + gsl_test(f, " QRc_lssolve vander(12)"); + s += f; + + return s; +} + +int +test_QRc_decomp_dim(const gsl_matrix_complex * m, double eps) +{ + int s = 0; + unsigned long i,j, M = m->size1, N = m->size2; + + gsl_matrix_complex * qr = gsl_matrix_complex_alloc(M,N); + gsl_matrix_complex * a = gsl_matrix_complex_alloc(M,N); + gsl_matrix_complex * q = gsl_matrix_complex_alloc(M,M); + gsl_matrix_complex * r = gsl_matrix_complex_alloc(M,N); + gsl_vector_complex * d = gsl_vector_complex_alloc(GSL_MIN(M,N)); + + gsl_matrix_complex_memcpy(qr,m); + + s += gsl_linalg_complex_QR_decomp(qr, d); + s += gsl_linalg_complex_QR_unpack(qr, d, q, r); + + /* compute a = q r */ + gsl_blas_zgemm (CblasNoTrans, CblasNoTrans, GSL_COMPLEX_ONE, q, r, GSL_COMPLEX_ZERO, a); + + for(i=0; i<M; i++) { + for(j=0; j<N; j++) { + gsl_complex aij = gsl_matrix_complex_get(a, i, j); + gsl_complex mij = gsl_matrix_complex_get(m, i, j); + int foo_r = check(GSL_REAL(aij), GSL_REAL(mij), eps); + int foo_i = check(GSL_IMAG(aij), GSL_IMAG(mij), eps); + if(foo_r || foo_i) { + printf("(%3lu,%3lu)[%lu,%lu]: %22.18g %22.18g\n", M, N, i,j, GSL_REAL(aij), GSL_REAL(mij)); + printf("(%3lu,%3lu)[%lu,%lu]: %22.18g %22.18g\n", M, N, i,j, GSL_IMAG(aij), GSL_IMAG(mij)); + } + s += foo_r + foo_i; + } + } + + gsl_vector_complex_free(d); + gsl_matrix_complex_free(qr); + gsl_matrix_complex_free(a); + gsl_matrix_complex_free(q); + gsl_matrix_complex_free(r); + + return s; +} + +int test_QRc_decomp(void) +{ + int f; + int s = 0; + + f = test_QRc_decomp_dim(m35c, 2 * 8.0 * GSL_DBL_EPSILON); + gsl_test(f, " QR_decomp m(3,5)"); + s += f; + + f = test_QRc_decomp_dim(m53c, 2 * 64.0 * GSL_DBL_EPSILON); + gsl_test(f, " QR_decomp m(5,3)"); + s += f; + + f = test_QRc_decomp_dim(m97c, 2 * 64.0 * GSL_DBL_EPSILON); + gsl_test(f, " QR_decomp m(9,7)"); + s += f; + + f = test_QRc_decomp_dim(vander2c, 8.0 * GSL_DBL_EPSILON); + gsl_test(f, " QRc_decomp vander(2)"); + s += f; + + f = test_QRc_decomp_dim(vander3c, 64.0 * GSL_DBL_EPSILON); + gsl_test(f, " QRc_decomp vander(3)"); + s += f; + + f = test_QRc_decomp_dim(vander4c, 1024.0 * GSL_DBL_EPSILON); + gsl_test(f, " QRc_decomp vander(4)"); + s += f; + + f = test_QRc_decomp_dim(vander12c, 0.0005); /* FIXME: bad accuracy */ + gsl_test(f, " QRc_decomp vander(12)"); + s += f; + + return s; +} + +int test_QRPT_lssolve_dim(const gsl_matrix * m, const double * actual, double eps) { int s = 0; @@ -3196,6 +3534,10 @@ main(void) m53 = create_general_matrix(5,3); m97 = create_general_matrix(9,7); + m35c = create_general_matrix_complex(3,5); + m53c = create_general_matrix_complex(5,3); + m97c = create_general_matrix_complex(9,7); + s35 = create_singular_matrix(3,5); s53 = create_singular_matrix(5,3); @@ -3209,6 +3551,11 @@ main(void) vander4 = create_vandermonde_matrix(4); vander12 = create_vandermonde_matrix(12); + vander2c = create_vandermonde_matrix_complex(2); + vander3c = create_vandermonde_matrix_complex(3); + vander4c = create_vandermonde_matrix_complex(4); + vander12c = create_vandermonde_matrix_complex(12); + moler10 = create_moler_matrix(10); c7 = create_complex_matrix(7); @@ -3256,7 +3603,9 @@ main(void) gsl_test(test_LUc_solve(r), "Complex LU Solve"); gsl_test(test_LUc_invert(r), "Complex LU Inverse"); gsl_test(test_QR_decomp(), "QR Decomposition"); + gsl_test(test_QRc_decomp(), "Complex QR Decomposition"); gsl_test(test_QR_solve(), "QR Solve"); + gsl_test(test_QRc_solve(), "Complex QR Solve"); gsl_test(test_LQ_solve(), "LQ Solve"); gsl_test(test_PTLQ_solve(), "PTLQ Solve"); @@ -3278,7 +3627,9 @@ main(void) gsl_test(test_PTLQ_solve(), "PTLQ Solve"); gsl_test(test_QR_QRsolve(), "QR QR Solve"); + gsl_test(test_QRc_QRsolve(), "Complex QR QR Solve"); gsl_test(test_QR_lssolve(), "QR LS Solve"); + gsl_test(test_QRc_lssolve(), "Complex QR LS Solve"); gsl_test(test_QR_update(), "QR Rank-1 Update"); gsl_test(test_QRPT_decomp(), "QRPT Decomposition"); gsl_test(test_QRPT_lssolve(), "QRPT LS Solve"); -- 2.11.0