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 (Sum-of-Squares). 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 37 of file nnls.c.

65 {
66 /* Check the parameters and data */
67 if(m<=0 || n<=0 || a==NULL || b==NULL || x==NULL) return(2);
68 if(rnorm!=NULL) *rnorm=nan("");
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 if(wp != NULL) {
229 if(npp1>=m) for(int ni=0; ni<n; ni++) w[ni]=0.;
230 }
231
232 /* Compute the norm of the final residual vector (sum-of-squares) */
233 if(rnorm != NULL) {
234 double ss=0.0;
235 if(npp1<m) for(int mi=npp1; mi<m; mi++) ss+=(b[mi]*b[mi]);
236 *rnorm=ss;
237 }
238
239 /* Free working space, if it was allocated here */
240//w=NULL; zz=NULL; index=NULL;
241 if(wl!=NULL) free(wl);
242 if(zzl!=NULL) free(zzl);
243 if(indexl!=NULL) free(indexl);
244 if(iter>=itmax) return(1);
245
246 return(0);
247} /* 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 257 of file nnls.c.

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

◆ 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 306 of file nnls.c.

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