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

NNLS (non-negative least squares) and required subroutines. More...

#include "libtpcmodel.h"

Go to the source code of this file.

Functions

int nnls (double **a, int m, int n, double *b, double *x, double *rnorm, double *wp, double *zzp, int *indexp)
 
int nnlsWght (int N, int M, double **A, double *b, double *weight)
 
int nnlsWghtSquared (int N, int M, double **A, double *b, double *sweight)
 

Detailed Description

NNLS (non-negative least squares) and required subroutines.

Author
Vesa Oikonen, Kaisa Sederholm

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.

Definition in file nnls.c.

Function Documentation

◆ nnls()

int nnls ( double ** a,
int m,
int n,
double * b,
double * x,
double * rnorm,
double * wp,
double * zzp,
int * indexp )

Algorithm NNLS (Non-negative least-squares).

Given an m by n matrix A, and an m-vector B, computes an n-vector X, that solves the least squares problem A * X = B , subject to X>=0

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

See also
qrLSQ, nnlsWghtSquared
Returns
Function returns 0 if successful, 1, if iteration count exceeded 3*N, or 2 in case of invalid problem dimensions or memory allocation error.
Parameters
aOn entry, a[ 0... N ][ 0 ... M ] contains the M by N matrix A. On exit, a[][] contains the product matrix Q*A, where Q is an m by n orthogonal matrix generated implicitly by this function.
mMatrix dimension m
nMatrix dimension n
bOn entry, b[] must contain the m-vector B. On exit, b[] contains Q*B
xOn exit, x[] will contain the solution vector
rnormOn exit, rnorm contains the squared Euclidean norm of the residual vector, R^2. If NULL is given, no rnorm is calculated
wpAn n-array of working space, wp[]. On exit, wp[] will contain the dual solution vector. wp[i]=0.0 for all i in set p and wp[i]<=0.0 for all i in set z. Can be NULL, which causes this algorithm to allocate memory for it.
zzpAn m-array of working space, zz[]. Can be NULL, which causes this algorithm to allocate memory for it.
indexpAn n-array of working space, index[]. Can be NULL, which causes this algorithm to allocate memory for it.

Definition at line 38 of file nnls.c.

66 {
67 /* Check the parameters and data */
68 if(m<=0 || n<=0 || a==NULL || b==NULL || x==NULL) return(2);
69 /* Allocate memory for working space, if required */
70 int *index=NULL, *indexl=NULL;
71 double *w=NULL, *zz=NULL, *wl=NULL, *zzl=NULL;
72 if(wp!=NULL) w=wp; else w=wl=(double*)calloc(n, sizeof(double));
73 if(zzp!=NULL) zz=zzp; else zz=zzl=(double*)calloc(m, sizeof(double));
74 if(indexp!=NULL) index=indexp; else index=indexl=(int*)calloc(n, sizeof(int));
75 if(w==NULL || zz==NULL || index==NULL) return(2);
76
77 /* Initialize the arrays INDEX[] and X[] */
78 for(int ni=0; ni<n; ni++) {x[ni]=0.; index[ni]=ni;}
79 int iz1=0;
80 int iz2=n-1;
81 int nsetp=0;
82 int npp1=0;
83
84 /* Main loop; quit if all coefficients are already in the solution or
85 if M cols of A have been triangulated */
86 double up=0.0;
87 int itmax; if(n<3) itmax=n*3; else itmax=n*n;
88 int iter=0;
89 int k, j=0, jj=0;
90 while(iz1<=iz2 && nsetp<m) {
91 /* Compute components of the dual (negative gradient) vector W[] */
92 for(int iz=iz1; iz<=iz2; iz++) {
93 int ni=index[iz];
94 double sm=0.;
95 for(int mi=npp1; mi<m; mi++) sm+=a[ni][mi]*b[mi];
96 w[ni]=sm;
97 }
98
99 double wmax;
100 int izmax=0;
101 while(1) {
102
103 /* Find largest positive W[j] */
104 wmax=0.0;
105 for(int iz=iz1; iz<=iz2; iz++) {
106 int i=index[iz];
107 if(w[i]>wmax) {wmax=w[i]; izmax=iz;}
108 }
109
110 /* Terminate if wmax<=0.; */
111 /* it indicates satisfaction of the Kuhn-Tucker conditions */
112 if(wmax<=0.0) break;
113 j=index[izmax];
114
115 /* The sign of W[j] is ok for j to be moved to set P.
116 Begin the transformation and check new diagonal element to avoid
117 near linear dependence. */
118 double asave=a[j][npp1];
119 up=0.0;
120 _lss_h12(1, npp1, npp1+1, m, &a[j][0], 1, &up, NULL, 1, 1, 0);
121 double unorm=0.0;
122 if(nsetp!=0) for(int mi=0; mi<nsetp; mi++) unorm+=a[j][mi]*a[j][mi];
123 unorm=sqrt(unorm);
124 double d=unorm+fabs(a[j][npp1])*0.01;
125 if((d-unorm)>0.0) {
126 /* Col j is sufficiently independent. Copy B into ZZ, update ZZ
127 and solve for ztest ( = proposed new value for X[j] ) */
128 for(int mi=0; mi<m; mi++) zz[mi]=b[mi];
129 _lss_h12(2, npp1, npp1+1, m, &a[j][0], 1, &up, zz, 1, 1, 1);
130 double ztest=zz[npp1]/a[j][npp1];
131 /* See if ztest is positive */
132 if(ztest>0.) break;
133 }
134
135 /* Reject j as a candidate to be moved from set Z to set P. Restore
136 A[npp1,j], set W[j]=0., and loop back to test dual coefficients again */
137 a[j][npp1]=asave; w[j]=0.;
138 } /* while(1) */
139 if(wmax<=0.0) break;
140
141 /* Index j=INDEX[izmax] has been selected to be moved from set Z to set P.
142 Update B and indices, apply householder transformations to cols in
143 new set Z, zero sub-diagonal elements in col j, set W[j]=0. */
144 for(int mi=0; mi<m; mi++) b[mi]=zz[mi];
145 index[izmax]=index[iz1]; index[iz1]=j; iz1++; nsetp=npp1+1; npp1++;
146 if(iz1<=iz2)
147 for(int jz=iz1; jz<=iz2; jz++) {
148 jj=index[jz];
149 _lss_h12(2, nsetp-1, npp1, m, &a[j][0], 1, &up, &a[jj][0], 1, m, 1);
150 }
151 if(nsetp!=m) for(int mi=npp1; mi<m; mi++) a[j][mi]=0.;
152 w[j]=0.;
153
154 /* Solve the triangular system; store the solution temporarily in Z[] */
155 for(int mi=0; mi<nsetp; mi++) {
156 int ip=nsetp-(mi+1);
157 if(mi!=0) for(int ii=0; ii<=ip; ii++) zz[ii]-=a[jj][ii]*zz[ip+1];
158 jj=index[ip]; zz[ip]/=a[jj][ip];
159 }
160
161 /* Secondary loop begins here */
162 while(++iter<itmax) {
163 /* See if all new constrained coefficients are feasible; if not, compute alpha */
164 double alpha=2.0;
165 for(int ip=0; ip<nsetp; ip++) {
166 int ni=index[ip];
167 if(zz[ip]<=0.) {
168 double t=-x[ni]/(zz[ip]-x[ni]);
169 if(alpha>t) {alpha=t; jj=ip-1;}
170 }
171 }
172
173 /* If all new constrained coefficients are feasible then still alpha==2.
174 If so, then exit from the secondary loop to main loop */
175 if(alpha==2.0) break;
176
177 /* Use alpha (0.<alpha<1.) to interpolate between old X and new ZZ */
178 for(int ip=0; ip<nsetp; ip++) {
179 int ni=index[ip]; x[ni]+=alpha*(zz[ip]-x[ni]);
180 }
181
182 /* Modify A and B and the INDEX arrays to move coefficient i from set P to set Z. */
183 int pfeas=1;
184 k=index[jj+1];
185 do {
186 x[k]=0.;
187 if(jj!=(nsetp-1)) {
188 jj++;
189 for(int ni=jj+1; ni<nsetp; ni++) {
190 int ii=index[ni]; index[ni-1]=ii;
191 double ss, cc;
192 _lss_g1(a[ii][ni-1], a[ii][ni], &cc, &ss, &a[ii][ni-1]);
193 a[ii][ni]=0.0;
194 for(int nj=0; nj<n; nj++) if(nj!=ii) {
195 /* Apply procedure G2 (CC,SS,A(J-1,L),A(J,L)) */
196 double temp=a[nj][ni-1];
197 a[nj][ni-1]=cc*temp+ss*a[nj][ni];
198 a[nj][ni]=-ss*temp+cc*a[nj][ni];
199 }
200 /* Apply procedure G2 (CC,SS,B(J-1),B(J)) */
201 double temp=b[ni-1]; b[ni-1]=cc*temp+ss*b[ni]; b[ni]=-ss*temp+cc*b[ni];
202 }
203 }
204 npp1=nsetp-1; nsetp--; iz1--; index[iz1]=k;
205
206 /* See if the remaining coefficients in set P are feasible; they should be
207 because of the way alpha was determined. If any are infeasible
208 it is due to round-off error. Any that are non-positive
209 will be set to zero and moved from set P to set Z. */
210 for(jj=0, pfeas=1; jj<nsetp; jj++) {
211 k=index[jj]; if(x[k]<=0.) {pfeas=0; break;}
212 }
213 } while(pfeas==0);
214
215 /* Copy B[] into zz[], then solve again and loop back */
216 for(int mi=0; mi<m; mi++) zz[mi]=b[mi];
217 for(int mi=0; mi<nsetp; mi++) {
218 int ip=nsetp-(mi+1);
219 if(mi!=0) for(int ii=0; ii<=ip; ii++) zz[ii]-=a[jj][ii]*zz[ip+1];
220 jj=index[ip]; zz[ip]/=a[jj][ip];
221 }
222 } /* end of secondary loop */
223
224 if(iter>=itmax) break;
225 for(int ip=0; ip<nsetp; ip++) {k=index[ip]; x[k]=zz[ip];}
226 } /* end of main loop */
227
228 /* Compute the norm of the final residual vector */
229 if(rnorm != NULL) {
230 double sm=0.0;
231 if(npp1<m) for(int mi=npp1; mi<m; mi++) sm+=(b[mi]*b[mi]);
232 else for(int ni=0; ni<n; ni++) w[ni]=0.;
233 *rnorm=sm; //sqrt(sm);
234 }
235
236 /* Free working space, if it was allocated here */
237 w=NULL; zz=NULL; index=NULL;
238 if(wl!=NULL) free(wl);
239 if(zzl!=NULL) free(zzl);
240 if(indexl!=NULL) free(indexl);
241 if(iter>=itmax) return(1);
242
243 return(0);
244} /* nnls */

Referenced by fitExpDecayNNLS(), and img_k1_using_ki().

◆ nnlsWght()

int nnlsWght ( int N,
int M,
double ** A,
double * b,
double * weight )

Algorithm for weighting the problem that is given to nnls-algorithm.

Square roots of weights are used because in nnls the difference w*A-w*b is squared.

Returns
Algorithm returns zero if successful, 1 if arguments are inappropriate.
See also
nnlsWghtSquared, nnls, qrLSQ, llsqWght
Parameters
NNNLS dimension N (nr of parameters).
MNNLS dimension M (nr of samples).
ANNLS matrix A.
bNNLS vector B.
weightWeights for each sample (array of length M).

Definition at line 254 of file nnls.c.

265 {
266 int n, m;
267 double *w;
268
269 /* Check the arguments */
270 if(N<1 || M<1 || A==NULL || b==NULL || weight==NULL) return(1);
271
272 /* Allocate memory */
273 w=(double*)malloc(M*sizeof(double)); if(w==NULL) return(2);
274
275 /* Check that weights are not zero and get the square roots of them to w[] */
276 for(m=0; m<M; m++) {
277 if(weight[m]<=1.0e-20) w[m]=0.0;
278 else w[m]=sqrt(weight[m]);
279 }
280
281 /* Multiply rows of matrix A and elements of vector b with weights*/
282 for(m=0; m<M; m++) {
283 for(n=0; n<N; n++) {
284 A[n][m]*=w[m];
285 }
286 b[m]*=w[m];
287 }
288
289 free(w);
290 return(0);
291}

◆ nnlsWghtSquared()

int nnlsWghtSquared ( int N,
int M,
double ** A,
double * b,
double * sweight )

Algorithm for weighting the problem that is given to nnls-algorithm.

Square roots of weights are used because in nnls the difference w*A-w*b is squared. Here user must give squared weights; this makes calculation faster, when this function needs to be called many times.

Returns
Algorithm returns zero if successful, 1 if arguments are inappropriate.
See also
nnlsWghtSquared, qrLSQ, nnls
Parameters
NNNLS dimension N (nr of parameters).
MNNLS dimension M (nr of samples).
ANNLS matrix A.
bNNLS vector B.
sweightSquared weights for each sample (array of length M).

Definition at line 303 of file nnls.c.

314 {
315 int n, m;
316
317 /* Check the arguments */
318 if(N<1 || M<1 || A==NULL || b==NULL || sweight==NULL) return(1);
319
320 /* Multiply rows of matrix A and elements of vector b with weights*/
321 for(m=0; m<M; m++) {
322 for(n=0; n<N; n++) {
323 A[n][m]*=sweight[m];
324 }
325 b[m]*=sweight[m];
326 }
327
328 return(0);
329}