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

NNLS (non-negative least squares) and required subroutines. 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 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 Liukko

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

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

Referenced by spectralDExp(), spectralDMSurge(), and tacDelayCMFit().

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

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

Referenced by spectralDExp(), and spectralDMSurge().

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

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