TPCCLIB
Loading...
Searching...
No Matches
qrlsq.c File Reference

QR decomposition for solving least squares problems. More...

#include "tpcclibConfig.h"
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "tpcextensions.h"
#include "tpclinopt.h"

Go to the source code of this file.

Functions

int qrLSQ (double **mat, double *rhs, double *sol, const unsigned int rows, const unsigned int cols, double *r2)
 QR least-squares solving routine.
int qr (double **A, int m, int n, double *B, double *X, double *rnorm, double *tau, double *res, double **wws, double *ws)
int qr_decomp (double **a, int M, int N, double *tau, double **cchain, double *chain)
int qr_solve (double **QR, int M, int N, double *tau, double *b, double *x, double *residual, double *resNorm, double **cchain, double *chain)
int qrWeight (int N, int M, double **A, double *b, double *weight, double *ws)
int qrWeightRm (int N, int M, double **A, double *b, double *weight, double *ws)
int qrSimpleLSQ (double **A, double *B, int M, int N, double *X, double *r2)
int qrLH (const unsigned int m, const unsigned int n, double *a, double *b, double *x, double *r2)
 Solve over-determined least-squares problem A x ~ b using successive Householder rotations.

Detailed Description

QR decomposition for solving least squares problems.

Author
Kaisa Liukko, Vesa Oikonen

Definition in file qrlsq.c.

Function Documentation

◆ qr()

int qr ( double ** A,
int m,
int n,
double * B,
double * X,
double * rnorm,
double * tau,
double * res,
double ** wws,
double * ws )

Algorithm QR.

Solves a matrix form least square problem min||A x - b|| => A x =~ b (A is m*n matrix, m>=n) using the QR decomposition for overdetermined systems. Based on GNU Scientific Library, edited by Kaisa Liukko.

Instead of pointers for working space, NULL can be given to let this function to allocate and free the required memory.

See also
qrLSQ, nnls, qr_decomp
Returns
Function returns 0 if successful and 1 in case of invalid problem dimensions or memory allocation error.
Parameters
AOn entry, a[m][n] contains the m by n matrix A. On exit, a[][] contains the QR factorization.
mDimensions of matrix A are a[m][n].
nDimensions of matrix A are a[m][n].
BB[] is an m-size vector containing the right-hand side vector b.
XOn exit, x[] will contain the solution vector x (size of n).
rnormOn exit, rnorm (pointer to double) contains the squared Euclidean norm of the residual vector (R^2); enter NULL if not needed.
tauOn exit, tau[] will contain the householder coefficients (size of n); enter NULL, if not needed.
resAn m-size array of working space, res[]. On output contains residual b - Ax. Enter NULL to let qr() to handle it.
wwsm*n array of working space. Enter NULL to let qr() to handle it.
ws2m-array of working space. Enter NULL to let qr() to handle it.

Definition at line 411 of file qrlsq.c.

436 {
437 int i;
438 double *qrRes, *qrTau, **qrWws, *qrWs, *chain;
439
440 /* Check the parameters and data */
441 if(m<=0 || n<=0 || A==NULL || B==NULL || X==NULL) return(1);
442 if(m<n) return(1);
443
444 /* Allocate memory for working space, if required */
445 if(tau!=NULL) qrTau=tau; else qrTau=(double*)calloc(n, sizeof(double));
446 if(res!=NULL) qrRes=res; else qrRes=(double*)calloc(m, sizeof(double));
447 if(wws!=NULL) {
448 qrWws=wws; chain=(double*)NULL;
449 } else {
450 qrWws=(double**)malloc(m * sizeof(double*));
451 chain=(double*)malloc(m*n * sizeof(double));
452 for(i=0; i<m; i++) qrWws[i]=chain + i*n;
453 }
454 if(ws!=NULL) qrWs=ws; else qrWs=(double*)calloc(2*m, sizeof(double));
455 if(qrTau==NULL || qrRes==NULL || qrWws==NULL || qrWs==NULL) return(1);
456
457 /* Form the householder decomposition and solve the least square problem */
458 if(qr_decomp(A, m, n, qrTau, qrWws, qrWs)) return(2);
459 if(qr_solve(A, m, n, qrTau, B, X, qrRes, rnorm, qrWws, qrWs)) return(3);
460
461 /* Free working space, if it was allocated here */
462 //if(tau==NULL || res==NULL || wws==NULL || ws==NULL) printf("free in qr()\n");
463 if(tau==NULL) free(qrTau);
464 if(res==NULL) free(qrRes);
465 if(wws==NULL) {free(qrWws); free(chain);}
466 if(ws==NULL) free(qrWs);
467 for(i=0; i<n; i++) if(isnan(X[i])) return(4);
468 return(0);
469} /* qr */
int qr_solve(double **QR, int M, int N, double *tau, double *b, double *x, double *residual, double *resNorm, double **cchain, double *chain)
Definition qrlsq.c:563
int qr_decomp(double **a, int M, int N, double *tau, double **cchain, double *chain)
Definition qrlsq.c:493

◆ qr_decomp()

int qr_decomp ( double ** a,
int M,
int N,
double * tau,
double ** cchain,
double * chain )

Factorise a general M x N matrix A into A = Q R , where Q is orthogonal (M x M) and R is upper triangular (M x N).

Q is stored as a packed set of Householder vectors in the strict lower triangular part of the input matrix A and a set of coefficients in vector tau.

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_1 Q_2 .. Q_k and it's transform as the product Q^T = Q_k .. Q_2 Q_1 , where k = min(M,N) and Q_i = (I - tau_i * h_i * h_i^T) and where h_i is a Householder vector h_i = [1, A(i+1,i), A(i+2,i), ... , A(M,i)]. This storage scheme is the same as in LAPACK.

NOTICE! The calling program must take care that pointer tau is of size N or M, whichever is smaller.

See also
qr
Returns
Function returns 0 if ok.
Parameters
acontains coefficient matrix A (m*n) as input and factorisation QR as output.
Mnr of rows in matrix A.
Nnr of columns in matrix A.
tauVector for householder coefficients, of length N or M, whichever is smaller.
cchainm*n array of working space.
chainm size array of working space.

Definition at line 493 of file qrlsq.c.

506 {
507 //printf("qr_decomp()\n");
508 int i, m, n, MNmin;
509 double *subvector, **submatrix;
510
511 /* Local variables */
512 if(M<N) MNmin=M; else MNmin=N;
513 if(MNmin<1 || a==NULL || tau==NULL || cchain==NULL || chain==NULL) return(1);
514 subvector=chain;
515 submatrix=cchain;
516
517 for(i=0; i<MNmin; i++) {
518 //printf("i=%d (MNmin=%d)\n", i, MNmin);
519 /* Compute the Householder transformation to reduce the j-th column of the matrix A to
520 a multiple of the j-th unit vector. Householder vector h_i is saved in the lower triangular
521 part of the column and Householder coefficient tau_i in the vector tau. */
522 for(m=i; m<M; m++) {
523 //printf("subvector[%d]=a[%d][%d]\n", m-1, m, i);
524 subvector[m-i]=a[m][i];
525 }
526 tau[i] = householder_transform(subvector, M-i);
527 for(m=i; m<M; m++) a[m][i]=subvector[m-i];
528
529 /* Apply the transformation to the remaining columns to get upper triangular part of matrix R */
530 if(i+1 < N) {
531 printf(" apply transformation\n");
532 for(m=i; m<M; m++) for(n=i+1; n<N; n++) {
533 if((m-i)<0 || (m-i)>=M || (n-i-1)<0 || (n-i-1)>=N) printf("OVERFLOW!\n");
534 submatrix[m-i][n-i-1]=a[m][n];
535 }
536 if(householder_hm(tau[i], subvector, submatrix, M-i, N-i)) return(2);
537 for(m=i; m<M; m++) for(n=i+1; n<N; n++)
538 a[m][n]=submatrix[m-i][n-i-1];
539 }
540 }
541
542 return(0);
543}
int householder_hm(double tau, double *vector, double **matrix, int rowNr, int columnNr)
Definition hholder.c:87
double householder_transform(double *v, int N)
Definition hholder.c:33

Referenced by qr().

◆ qr_solve()

int qr_solve ( double ** QR,
int M,
int N,
double * tau,
double * b,
double * x,
double * residual,
double * resNorm,
double ** cchain,
double * chain )

Find the least squares solution to the overdetermined system

A x = b

for m >= n using the QR factorisation A = Q R. qr_decomp() must be used prior to this function in order to form the QR factorisation of A. Solution is formed in the following order: QR x = b => R x = Q^T b => x = R^-1 (Q^T b)

NOTICE! The calling program must take care that pointers b, x and residual are of the right size.

See also
qr, qr_decomp
Returns
Function returns 0 if ok.
Parameters
QRm*n matrix containing householder vectors of A.
Mnr of rows in matrix A.
Nnr of columns in matrix A.
tauvector containing householder coefficients tau; length is M or N, whichever is smaller.
bContains m-size vector b of A x = b.
xsolution vector x of length n.
residualresidual vector of length m.
resNormnorm^2 of the residual vector; enter NULL if not needed.
cchainm*n array of the working space.
chain2m length array of the working space.

Definition at line 563 of file qrlsq.c.

584 {
585 //printf("qr_solve()\n");
586 if(QR==NULL || tau==NULL || b==NULL || x==NULL || residual==NULL) return(1);
587 if(cchain==NULL || chain==NULL) return(1);
588 if(M<1 || N<1) return(2);
589
590 int m, n;
591 double **Rmatrix;
592
593 /* Local variable */
594 Rmatrix=cchain;
595
596 /* Get matrix R from the upper triangular part of QR
597 First the rows N - M-1 are eliminated from R matrix*/
598 for(m=0; m<N; m++) for(n=0; n<N; n++) Rmatrix[m][n]=QR[m][n];
599 for(m=0; m<M; m++) residual[m]=b[m];
600
601 /* Compute b = Q^T b */
602 if(qr_QTvec(M, N, QR, tau, residual, chain)) return(3);
603
604 /* Solve R x = b by computing x = R^-1 b */
605 for(n=0; n<N; n++) x[n]=residual[n];
606 if(qr_invRvec(N, Rmatrix, x)) return(4);
607
608 /* Compute residual = b - A x = Q (Q^T b - R x) */
609 for(n=0; n<N; n++) residual[n]=0.0;
610 /* Compute residual= Q*residual */
611 if(qr_Qvec(M, N, QR, tau, residual, chain)) return(4);
612
613 /* Compute norm^2 of the residual vector, if needed */
614 if(resNorm!=NULL)
615 for(m=0, *resNorm=0.0; m<M; m++) *resNorm +=residual[m]*residual[m];
616
617 return(0);
618}

Referenced by qr().

◆ qrLH()

int qrLH ( const unsigned int m,
const unsigned int n,
double * a,
double * b,
double * x,
double * r2 )

Solve over-determined least-squares problem A x ~ b using successive Householder rotations.

This routine is based on the text and Fortran code in C.L. Lawson and R.J. Hanson, Solving Least Squares Problems, Prentice-Hall, Englewood Cliffs, New Jersey, 1974, and Fortran code by R.L. Parker and P.B. Stark.

Returns
Returns 0 when successful, 1 if system is singular, and 2 in case of other errors, including that system is under-determined.
Parameters
mNumber of samples in matrix A and the length of vector b.
nNumber of parameters in matrix A and the length of vector x. The n must be smaller or equal to m.
aPointer to matrix A; matrix must be given as an n*m array, containing n consecutive m-length vectors. Contents of A are modified in this routine.
bPointer to vector b of length n. Contents of b are modified in this routine.
xPointer to the result vector x of length n.
r2Pointer to a double value, in where the sum of squared residuals is written.

Definition at line 946 of file qrlsq.c.

963 {
964 /* Check the input */
965 if(a==NULL || b==NULL || x==NULL || r2==NULL) return(2);
966 if(n<1 || m<n) {*r2=nan(""); return(2);}
967
968 /* Initiate output to zeroes, in case of exit because of singularity */
969 for(unsigned int ni=0; ni<n; ni++) x[ni]=0.0;
970 *r2=0.0;
971
972 /* Rotates matrix A into upper triangular form */
973 for(unsigned int ni=0; ni<n; ni++) {
974 /* Find constants for rotation and diagonal entry */
975 double sq=0.0;
976 for(unsigned int mi=ni; mi<m; mi++) sq+=a[mi + ni*m]*a[mi + ni*m];
977 if(sq==0.0) return(1);
978 double qv1=-copysign(sqrt(sq), a[ni + ni*m]);
979 double u1=a[ni + ni*m] - qv1;
980 a[ni + ni*m]=qv1;
981 unsigned int ni1=ni+1;
982 /* Rotate the remaining columns of sub-matrix. */
983 for(unsigned int nj=ni1; nj<n; nj++) {
984 double dot=u1*a[ni + nj*m];
985 for(unsigned int mi=ni1; mi<m; mi++)
986 dot+=a[mi + nj*m] * a[mi + ni*m];
987 double c=dot/fabs(qv1*u1);
988 for(unsigned int mi=ni1; mi<m; mi++)
989 a[mi + nj*m]-=c*a[mi + ni*m];
990 a[ni + nj*m]-=c*u1;
991 }
992 /* Rotate vector B */
993 double dot=u1*b[ni];
994 for(unsigned int mi=ni1; mi<m; mi++)
995 dot+=b[mi]*a[mi + ni*m];
996 double c=dot/fabs(qv1*u1);
997 b[ni]-=c*u1;
998 for(unsigned int mi=ni1; mi<m; mi++)
999 b[mi]-=c*a[mi + ni*m];
1000 } // end of rotation loop
1001
1002 /* Solve triangular system by back-substitution. */
1003 for(unsigned int ni=0; ni<n; ni++) {
1004 int k=n-ni-1;
1005 double s=b[k];
1006 for(unsigned int nj=k+1; nj<n; nj++)
1007 s-=a[k + nj*m] * x[nj];
1008 if(a[k + k*m]==0.0) return(1);
1009 x[k]=s/a[k + k*m];
1010 }
1011
1012 /* Calculate the sum of squared residuals. */
1013 *r2=0.0;
1014 for(unsigned int mi=n; mi<m; mi++)
1015 *r2 += b[mi]*b[mi];
1016
1017 return(0);
1018}

Referenced by bvls().

◆ qrLSQ()

int qrLSQ ( double ** mat,
double * rhs,
double * sol,
const unsigned int rows,
const unsigned int cols,
double * r2 )

QR least-squares solving routine.

Solves a matrix form least square problem min||A x - b|| => A x =~ b (A is m*n matrix, m>=n) using the QR decomposition for overdetermined systems

Author
: Michael Mazack License: Public Domain. Redistribution and modification without restriction is granted. If you find this code helpful, please let the author know (https://mazack.org). Small editions by Vesa Oikonen when added to tpcclib.
See also
nnls, qr
Todo
Check that modification in one subroutine is ok, and add possibility to provide working space for the main and sub-functions.
Returns
Returns 0 if ok.
Parameters
matPointer to the row-major matrix (2D array), A[rows][cols]; not modified.
rhsVector b[rows]; modified to contain the computed (fitted) b[], that is, matrix-vector product A*x.
solSolution vector x[cols]; solution x corresponds to the solution of both the modified and original systems A and B.
rowsNumber of rows (samples) in matrix A and length of vector B.
colsNumber of cols (parameters) in matrix A and length of vector X.
r2Pointer to value where R^2, the difference between original and computed right hand side (b); enter NULL if not needed.

Definition at line 72 of file qrlsq.c.

88 {
89 if(rows<1 || cols<1) return(1);
90 if(mat==NULL || rhs==NULL || sol==NULL) return(2);
91
92 unsigned int i, j, max_loc;
93 unsigned int *p=NULL;
94 double *buf=NULL, *ptr, *v=NULL, *orig_b=NULL, **A=NULL;
95
96 /* Allocate memory for index vector and Householder transform vector */
97 /* and original data matrix and vector. */
98 p=malloc(sizeof(unsigned int)*cols);
99 A=malloc(sizeof(double*)*rows);
100 buf=malloc(sizeof(double)*(rows*cols+rows+rows));
101 if(p==NULL || A==NULL || buf==NULL) {
102 free(p); free(A); free(buf);
103 return(3);
104 }
105 ptr=buf;
106 for(i=0; i<rows; i++) {A[i]=ptr; ptr+=cols;}
107 v=ptr; ptr+=rows; orig_b=ptr;
108 /* copy the original data */
109 for(i=0; i<rows; i++) {
110 orig_b[i]=rhs[i];
111 for(j=0; j<cols; j++) A[i][j]=mat[i][j];
112 }
113
114 /* Initial permutation vector. */
115 for(i=0; i<cols; i++) p[i]=i;
116
117 /* Apply rotators to make R and Q'*b */
118 for(i=0; i<cols; i++) {
119 if(qr_get_next_col(A, rows, cols, i, p, &max_loc)==0)
120 qr_swap_cols(p, i, max_loc);
121 qr_householder(A, rows, cols, i, p[i], v);
122 qr_apply_householder(A, rhs, rows, cols, v, i, p);
123 }
124
125 /* Back solve Rx = Q'*b */
126 qr_back_solve(A, rhs, rows, cols, sol, p);
127
128 /* Compute fitted b[] (matrix-vector product A*x) using non-modified A */
129 for(i=0; i<rows; i++) {
130 rhs[i]=0.0;
131 for(j=0; j<cols; j++) rhs[i]+=mat[i][j]*sol[j];
132 }
133
134 /* Compute R^2, if requested */
135 if(r2!=NULL) {
136 double ss=0, d;
137 for(i=0; i<rows; i++) {
138 d=orig_b[i]-rhs[i];
139 ss+=d*d;
140 }
141 *r2=ss;
142 }
143
144 /* Collect garbage. */
145 free(p); free(A); free(buf);
146 return(0);
147}

◆ qrSimpleLSQ()

int qrSimpleLSQ ( double ** A,
double * B,
int M,
int N,
double * X,
double * r2 )

Simplistic QR least-squares solving routine.

Reference: Máté A. Introduction to Numerical Analysis with C programs. Chapter 38. Overdetermined systems of linear equations. Brooklyn College of the City University of New York, 2014.

See also
qrLSQ
Returns
Function returns 0 when successful.
Parameters
AMatrix A[M][N].
BVector B[M].
Mnr of rows in matrix A, must be >=N.
Nnr of columns in matrix A, must be <=M.
XVector X of length N for the results.
r2Pointer to value where R^2, the difference between original and computed right hand side (b); enter NULL if not needed.

Definition at line 854 of file qrlsq.c.

868 {
869 //printf("qrSimpleLSQ()\n");
870 if(A==NULL || B==NULL || X==NULL || N<1 || M<N) return(1);
871
872 const double closeZero=1.0E-20;
873
874 double cc[M], orig_B[M];
875 double p[M][N], c[M][N];
876
877 /* Householder transformation of matrix A into matrix P */
878 for(int n=0; n<N; n++) {
879 for(int nn=0; nn<n; nn++) p[nn][n]=0.0;
880 p[n][n]=0.0;
881 for(int m=n; m<M; m++) p[n][n]+=A[m][n]*A[m][n];
882 p[n][n]=copysign(sqrt(p[n][n]), A[n][n]);
883 p[n][n]+=A[n][n];
884 if(fabs(p[n][n])<closeZero) return(2);
885 for(int m=n+1; m<M; m++) p[m][n]=A[m][n];
886 double norm=0.0;
887 for(int m=n; m<M; m++) norm+=p[m][n]*p[m][n];
888 norm=sqrt(norm);
889 for(int m=n; m<M; m++) p[m][n]/=norm;
890 for(int m=n; m<M; m++) {
891 for(int nn=n; nn<N; nn++) {
892 c[m][nn]=A[m][nn];
893 for(int mm=n; mm<M; mm++) c[m][nn]-=2.0*p[m][n]*p[mm][n]*A[mm][nn];
894 }
895 }
896 for(int m=n; m<M; m++)
897 for(int nn=n; nn<N; nn++) A[m][nn]=c[m][nn];
898 }
899
900 /* Keep the original B[] for R^2 calculation */
901 for(int m=0; m<M; m++) orig_B[m]=B[m];
902
903 /* Compute P'b */
904 for(int n=0; n<N; n++) {
905 for(int m=n; m<M; m++) {
906 cc[m]=B[m];
907 for(int mm=n; mm<M; mm++) cc[m]-=2.0*p[m][n]*p[mm][n]*B[mm];
908 }
909 for(int m=n; m<M; m++) B[m]=cc[m];
910 }
911
912 /* Solve the linear system with backward substitution */
913 X[N-1]=B[N-1]/A[N-1][N-1];
914 for(int n=N-2; n>=0; n--) {
915 X[n]=B[n];
916 for(int nn=n+1; nn<N; nn++) X[n]-=A[n][nn]*X[nn];
917 X[n]/=A[n][n];
918 }
919
920 /* Compute R^2, if requested */
921 if(r2!=NULL) {
922 double ss=0, d;
923 for(int m=0; m<M; m++) {
924 d=orig_B[m]-B[m];
925 ss+=d*d;
926 }
927 *r2=ss;
928 }
929
930 return(0);
931}

◆ qrWeight()

int qrWeight ( int N,
int M,
double ** A,
double * b,
double * weight,
double * ws )

Algorithm for weighting the problem that is given to QR algorithm. Square roots of weights are used because in QR the difference w*A-w*b is squared.

See also
qrWeightRm, qrLSQ, qr
Todo
Add tests.
Returns
Algorithm returns zero if successful, otherwise <>0.
Parameters
NDimensions of matrix A are a[m][n].
MDimensions of matrix A are a[m][n]; size of vector B is m.
AMatrix a[m][n] for QR, contents will be weighted here.
bB[] is an m-size vector for QR, contents will be weighted here.
weightPointer to array of size m, which contains sample weights, used here to weight matrix A and vector b.
wsm-sized vector for working space; enter NULL to allocate locally.

Definition at line 745 of file qrlsq.c.

759 {
760 int n, m;
761 double *w;
762
763 /* Check the arguments */
764 if(N<1 || M<1 || A==NULL || b==NULL || weight==NULL) return(1);
765
766 /* Allocate memory, if necessary */
767 if(ws==NULL) {
768 w=(double*)malloc(M*sizeof(double)); if(w==NULL) return(2);
769 } else {
770 w=ws;
771 }
772
773 /* Check that weights are not zero and get the square roots of them to w[]. */
774 for(m=0; m<M; m++) {
775 if(weight[m]<=1.0e-100) w[m]=1.0e-50;
776 else w[m]=sqrt(weight[m]);
777 }
778
779 /* Multiply rows of matrix A and elements of vector b with weights. */
780 for(m=0; m<M; m++) {
781 for(n=0; n<N; n++) A[m][n]*=w[m];
782 b[m]*=w[m];
783 }
784
785 if(ws==NULL) free(w);
786 return(0);
787}

◆ qrWeightRm()

int qrWeightRm ( int N,
int M,
double ** A,
double * b,
double * weight,
double * ws )

Remove the weights from data provided to QR LSQ algorithms.

See also
qrWeight, qrLSQ, qr
Todo
Add tests.
Returns
Algorithm returns zero if successful, otherwise <>0.
Parameters
NDimensions of matrix A are a[m][n].
MDimensions of matrix A are a[m][n]; size of vector B is m.
AMatrix a[m][n] for QR, weights will be removed here; enter NULL if not needed.
bB[] is an m-size vector for QR, weights will be removed here; enter NULL if not needed here.
weightPointer to array of size m, which contains sample weights, used here to weight matrix A and vector b.
wsm-sized vector for working space; enter NULL to allocate locally

Definition at line 796 of file qrlsq.c.

811 {
812 int n, m;
813 double *w;
814
815 /* Check the arguments */
816 if(N<1 || M<1 || weight==NULL) return(1);
817 if(A==NULL && b==NULL) return(0); // nothing to do
818
819 /* Allocate memory, if necessary */
820 if(ws==NULL) {
821 w=(double*)malloc(M*sizeof(double)); if(w==NULL) return(2);
822 } else {
823 w=ws;
824 }
825
826 /* Check that weights are not zero and get the square roots of them to w[] */
827 for(m=0; m<M; m++) {
828 if(weight[m]<=1.0e-100) w[m]=1.0e-50;
829 else w[m]=sqrt(weight[m]);
830 }
831
832 /* Multiply rows of matrix A and elements of vector b with weights */
833 for(m=0; m<M; m++) {
834 if(A!=NULL) for(n=0; n<N; n++) A[m][n]/=w[m];
835 if(b!=NULL) b[m]/=w[m];
836 }
837
838 if(ws==NULL) free(w);
839 return(0);
840}