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

Routines needed in the use of QR decomposition when solving least squares problems. More...

#include "libtpcmodel.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 qr_weight (int N, int M, double **A, double *b, double *weight, double *ws)
 
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

Routines needed in the use of QR decomposition when solving least squares problems.

Author
Kaisa Sederholm, Vesa Oikonen

These routines are based on the code of Gerard Jungman and Brian Gough provided in the GSL library (https://sources.redhat.com/gsl/).

Definition in file qr.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.

Returns
Function returns 0 if successful and 1 in case of invalid problem dimensions or memory allocation error.
See also
qr_decomp, qr_solve, qrLH
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 346 of file qr.c.

371 {
372 int i;
373 double *qrRes, *qrTau, **qrWws, *qrWs, *chain;
374
375 /* Check the parameters and data */
376 if(m<=0 || n<=0 || A==NULL || B==NULL || X==NULL) return(1);
377 if(m<n) return(1);
378
379 /* Allocate memory for working space, if required */
380 if(tau!=NULL) qrTau=tau; else qrTau=(double*)calloc(n, sizeof(double));
381 if(res!=NULL) qrRes=res; else qrRes=(double*)calloc(m, sizeof(double));
382 if(wws!=NULL) {
383 qrWws=wws; chain=(double*)NULL;
384 } else {
385 qrWws=(double**)malloc(m * sizeof(double*));
386 chain=(double*)malloc(m*n * sizeof(double));
387 for(i=0; i<m; i++) qrWws[i]=chain + i*n;
388 }
389 if(ws!=NULL) qrWs=ws; else qrWs=(double*)calloc(2*m, sizeof(double));
390 if(qrTau==NULL || qrRes==NULL || qrWws==NULL || qrWs==NULL) return(1);
391
392 /* Form the householder decomposition and solve the least square problem */
393 if(qr_decomp(A, m, n, qrTau, qrWws, qrWs)) return(2);
394 if(qr_solve(A, m, n, qrTau, B, X, qrRes, rnorm, qrWws, qrWs)) return(3);
395
396 /* Free working space, if it was allocated here */
397 if(tau==NULL) free(qrTau);
398 if(res==NULL) free(qrRes);
399 if(wws==NULL) {free(qrWws); free(chain);}
400 if(ws==NULL) free(qrWs);
401 for(i=0; i<n; i++) if(isnan(X[i])) return(4);
402 return(0);
403} /* 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 qr.c:492
int qr_decomp(double **a, int M, int N, double *tau, double **cchain, double *chain)
Definition qr.c:427

◆ 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.

Returns
Function returns 0 if ok.
See also
qr_solve
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 matrix of working space.
chainm size array of working space.

Definition at line 427 of file qr.c.

440 {
441 //printf("qr_decomp()\n");
442 int i, m, n, MNmin;
443 double *subvector, **submatrix;
444
445 /* Local variables */
446 if(M<N) MNmin=M; else MNmin=N;
447 if(MNmin<1 || a==NULL || tau==NULL || cchain==NULL || chain==NULL) return(1);
448
449 subvector=chain;
450 submatrix=cchain;
451
452 for(i=0; i<MNmin; i++) {
453 //printf("i=%d (MNmin=%d)\n", i, MNmin);
454 /* Compute the Householder transformation to reduce the j-th column of the matrix A to
455 a multiple of the j-th unit vector. Householder vector h_i is saved in the lower triangular
456 part of the column and Householder coefficient tau_i in the vector tau. */
457 for(m=i; m<M; m++) subvector[m-i]=a[m][i];
458 tau[i] = householder_transform(subvector, M-i);
459 for(m=i; m<M; m++) a[m][i]=subvector[m-i];
460
461 /* Apply the transformation to the remaining columns to get upper triangular part of matrix R */
462 if(i+1 < N) {
463 for(m=i; m<M; m++)
464 for(n=i+1; n<N; n++)
465 submatrix[m-i][n-i-1]=a[m][n];
466 if(householder_hm(tau[i], subvector, submatrix, M-i, N-i)) return(2);
467 for(m=i; m<M; m++)
468 for(n=i+1; n<N; n++)
469 a[m][n]=submatrix[m-i][n-i-1];
470 }
471 }
472
473 return(0);
474}
int householder_hm(double tau, double *vector, double **matrix, int rowNr, int columnNr)
Definition hholder.c:68
double householder_transform(double *v, int N)
Definition hholder.c:23

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.

Returns
Function returns 0 if ok.
Precondition
qr_decomp
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 matrix of the working space.
chain2m length array of the working space.

Definition at line 492 of file qr.c.

513 {
514 //printf("qr_solve()\n");
515 if(QR==NULL || tau==NULL || b==NULL || x==NULL || residual==NULL) return(1);
516 if(cchain==NULL || chain==NULL) return(1);
517 if(M<1 || N<1) return(2);
518
519 int MNmin; if(M<N) MNmin=M; else MNmin=N;
520
521 int m, n;
522 double **Rmatrix=cchain;
523 double *h=chain;
524 double *w=chain+M;
525
526 /* Get matrix R from the upper triangular part of QR
527 First the rows N - M-1 are eliminated from R matrix*/
528 for(m=0; m<N; m++) for(n=0; n<N; n++) Rmatrix[m][n]=QR[m][n];
529 for(m=0; m<M; m++) residual[m]=b[m];
530
531 /* Compute b = Q^T b */
532 /* Form the product Q^T residual from householder vectors saved in the lower triangle of
533 QR matrix and householder coefficients saved in vector tau. */
534 for(int i=0; i<MNmin; i++) {
535 for(m=i; m<M; m++) h[m-i]=QR[m][i];
536 for(m=i; m<M; m++) w[m-i]=residual[m];
537 if(householder_hv(tau[i], M-i, h, w)) return(2);
538 for(m=i; m<M; m++) residual[m]=w[m-i];
539 }
540
541 /* Solve R x = b by computing x = R^-1 b */
542 for(n=0; n<N; n++) x[n]=residual[n];
543 /* back-substitution */
544 x[N-1]=x[N-1]/Rmatrix[N-1][N-1];
545 for(int i=N-2; i>=0; i--) {
546 for(int j=i+1; j<N; j++) x[i]-=Rmatrix[i][j]*x[j];
547 x[i]/=Rmatrix[i][i];
548 }
549
550 /* Compute residual = b - A x = Q (Q^T b - R x) */
551 for(n=0; n<N; n++) residual[n]=0.0;
552 /* Compute residual= Q*residual */
553 /* Form the product Q*residual from householder vectors saved in the lower triangle
554 of QR matrix and householder coefficients saved in vector tau. */
555 for(int i=MNmin-1; i>=0; i--) {
556 for(int m=i; m<M; m++) h[m-i]=QR[m][i];
557 for(int m=i; m<M; m++) w[m-i]=residual[m];
558 if(householder_hv(tau[i], M-i, h, w)) return(2);
559 for(int m=i; m<M; m++) residual[m]=w[m-i];
560 }
561
562 /* Compute norm^2 of the residual vector, if needed */
563 if(resNorm!=NULL)
564 for(m=0, *resNorm=0.0; m<M; m++) *resNorm +=residual[m]*residual[m];
565
566 return(0);
567}
int householder_hv(double tau, int size, double *v, double *w)
Definition hholder.c:107

Referenced by qr().

◆ qr_weight()

int qr_weight ( 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.

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 576 of file qr.c.

590 {
591 int n, m;
592 double *w;
593
594 /* Check the arguments */
595 if(N<1 || M<1 || A==NULL || b==NULL || weight==NULL) return(1);
596
597 /* Allocate memory, if necessary */
598 if(ws==NULL) {
599 w=(double*)malloc(M*sizeof(double)); if(w==NULL) return(2);
600 } else {
601 w=ws;
602 }
603
604 /* Check that weights are not zero and get the square roots of them to w[]. */
605 for(m=0; m<M; m++) {
606 if(weight[m]<=1.0e-100) w[m]=1.0e-50;
607 else w[m]=sqrt(weight[m]);
608 }
609
610 /* Multiply rows of matrix A and elements of vector b with weights. */
611 for(m=0; m<M; m++) {
612 for(n=0; n<N; n++) A[m][n]*=w[m];
613 b[m]*=w[m];
614 }
615
616 if(ws==NULL) free(w);
617 return(0);
618}

◆ 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 633 of file qr.c.

650 {
651 /* Check the input */
652 if(a==NULL || b==NULL || x==NULL || r2==NULL) return(2);
653 if(n<1 || m<n) {*r2=nan(""); return(2);}
654
655 /* Initiate output to zeroes, in case of exit because of singularity */
656 for(unsigned int ni=0; ni<n; ni++) x[ni]=0.0;
657 *r2=0.0;
658
659 /* Rotates matrix A into upper triangular form */
660 for(unsigned int ni=0; ni<n; ni++) {
661 /* Find constants for rotation and diagonal entry */
662 double sq=0.0;
663 for(unsigned int mi=ni; mi<m; mi++) sq+=a[mi + ni*m]*a[mi + ni*m];
664 if(sq==0.0) return(1);
665 double qv1=-copysign(sqrt(sq), a[ni + ni*m]);
666 double u1=a[ni + ni*m] - qv1;
667 a[ni + ni*m]=qv1;
668 unsigned int ni1=ni+1;
669 /* Rotate the remaining columns of sub-matrix. */
670 for(unsigned int nj=ni1; nj<n; nj++) {
671 double dot=u1*a[ni + nj*m];
672 for(unsigned int mi=ni1; mi<m; mi++)
673 dot+=a[mi + nj*m] * a[mi + ni*m];
674 double c=dot/fabs(qv1*u1);
675 for(unsigned int mi=ni1; mi<m; mi++)
676 a[mi + nj*m]-=c*a[mi + ni*m];
677 a[ni + nj*m]-=c*u1;
678 }
679 /* Rotate vector B */
680 double dot=u1*b[ni];
681 for(unsigned int mi=ni1; mi<m; mi++)
682 dot+=b[mi]*a[mi + ni*m];
683 double c=dot/fabs(qv1*u1);
684 b[ni]-=c*u1;
685 for(unsigned int mi=ni1; mi<m; mi++)
686 b[mi]-=c*a[mi + ni*m];
687 } // end of rotation loop
688
689 /* Solve triangular system by back-substitution. */
690 for(unsigned int ni=0; ni<n; ni++) {
691 int k=n-ni-1;
692 double s=b[k];
693 for(unsigned int nj=k+1; nj<n; nj++)
694 s-=a[k + nj*m] * x[nj];
695 if(a[k + k*m]==0.0) return(1);
696 x[k]=s/a[k + k*m];
697 }
698
699 /* Calculate the sum of squared residuals. */
700 *r2=0.0;
701 for(unsigned int mi=n; mi<m; mi++)
702 *r2 += b[mi]*b[mi];
703
704 return(0);
705}

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 54 of file qr.c.

70 {
71 if(rows<1 || cols<1) return(1);
72 if(mat==NULL || rhs==NULL || sol==NULL) return(2);
73
74 unsigned int i, j, max_loc;
75 unsigned int *p=NULL;
76 double *buf=NULL, *ptr, *v=NULL, *orig_b=NULL, **A=NULL;
77
78 /* Allocate memory for index vector and Householder transform vector */
79 /* and original data matrix and vector. */
80 p=malloc(sizeof(unsigned int)*cols);
81 A=malloc(sizeof(double*)*rows);
82 buf=malloc(sizeof(double)*(rows*cols+rows+rows));
83 if(p==NULL || A==NULL || buf==NULL) {
84 free(p); free(A); free(buf);
85 return(3);
86 }
87 ptr=buf;
88 for(i=0; i<rows; i++) {A[i]=ptr; ptr+=cols;}
89 v=ptr; ptr+=rows; orig_b=ptr;
90 /* copy the original data */
91 for(i=0; i<rows; i++) {
92 orig_b[i]=rhs[i];
93 for(j=0; j<cols; j++) A[i][j]=mat[i][j];
94 }
95
96 /* Initial permutation vector. */
97 for(i=0; i<cols; i++) p[i]=i;
98
99 /* Apply rotators to make R and Q'*b */
100 for(i=0; i<cols; i++) {
101 if(qr_get_next_col(A, rows, cols, i, p, &max_loc)==0)
102 qr_swap_cols(p, i, max_loc);
103 qr_householder(A, rows, cols, i, p[i], v);
104 qr_apply_householder(A, rhs, rows, cols, v, i, p);
105 }
106
107 /* Back solve Rx = Q'*b */
108 qr_back_solve(A, rhs, rows, cols, sol, p);
109
110 /* Compute fitted b[] (matrix-vector product A*x) using non-modified A */
111 for(i=0; i<rows; i++) {
112 rhs[i]=0.0;
113 for(j=0; j<cols; j++) rhs[i]+=mat[i][j]*sol[j];
114 }
115
116 /* Compute R^2, if requested */
117 if(r2!=NULL) {
118 double ss=0, d;
119 for(i=0; i<rows; i++) {
120 d=orig_b[i]-rhs[i];
121 ss+=d*d;
122 }
123 *r2=ss;
124 }
125
126 /* Collect garbage. */
127 free(p); free(A); free(buf);
128 return(0);
129}