TPCCLIB
Loading...
Searching...
No Matches
nnlsq.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 nnlsq (NNLSQDATA *d, int verbose)
void nnlsqDataInit (NNLSQDATA *d)
void nnlsqDataFree (NNLSQDATA *d)
int nnlsqDataAllocate (NNLSQDATA *d, const int n, const int m)
int nnlsqWght (NNLSQDATA *d, double *weight)
int nnlsqWghtSquared (NNLSQDATA *d, double *sweight)

Detailed Description

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

Author
Vesa Oikonen

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.

Note
Contents of this file are currently under development and testing, and functions in nnls.c should be used instead.

Definition in file nnlsq.c.

Function Documentation

◆ nnlsq()

int nnlsq ( NNLSQDATA * d,
int verbose )

Algorithm NNLS (Non-negative least-squares).

The same algorithm as nnls(), but this one gets its data in a struct, and max iteration number and dependence factor (previously fixed to 0.01) can be set as function parameters in the struct.

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
nnlsqDataInit, nnlsqDataAllocate, nnlsqDataFree, nnlsqWghtSquared, nnls, qrLSQ
Returns
Function returns 0 if successful, 1, if iteration count exceeded specified limit, 2 in case of invalid problem dimensions, and >2 in case of other errors.
Parameters
dPointer to structure containing the data and place for the results.
Precondition
Initiate the structure, allocate memory for the data, and possibly weight the data.
Postcondition
Free the allocated memory.
See also
nnlsqDataInit, nnlsqDataAllocate, nnlsqDataFree
Parameters
verboseVerbose level; if zero, then nothing is printed to stderr or stdout.

Definition at line 55 of file nnlsq.c.

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

◆ nnlsqDataAllocate()

int nnlsqDataAllocate ( NNLSQDATA * d,
const int n,
const int m )

Allocate memory for NNLSQDATA (and set data pointers inside the structure). Any previous contents are deleted. Contents are set to NaN or zero.

See also
nnlsDataInit, nnlsqDataFree, nnlsq, nnlsqWght
Returns
Returns TPCERROR status (0 when successful).
Parameters
dPointer to initiated structure; any old contents are deleted.
nNumber of parameters (columns of matrix A).
mNumber of samples (rows of matrix A).

Definition at line 411 of file nnlsq.c.

418 {
419 if(d==NULL) return(TPCERROR_FAIL);
420 /* Delete any previous contents */
421 nnlsqDataFree(d);
422 /* If no memory is requested, then return fail */
423 if(n<1 || m<1) return(TPCERROR_FAIL);
424
425 /* Allocate memory for all double arrays and matrix */
426 int s = n*m + m + n + n + m;
427#ifdef TEST_BUFOVERFLOW
428 s+=5;
429#endif
430 d->_data=(double*)malloc(sizeof(double)*s);
431 if(d->_data==NULL) return(TPCERROR_OUT_OF_MEMORY);
432 for(int i=0; i<s; i++) d->_data[i]=nan("");
433 /* Allocate memory for matrix pointers */
434 d->a=(double**)malloc(sizeof(double*)*n);
435 if(d->a==NULL) {free(d->_data); return(TPCERROR_OUT_OF_MEMORY);}
436 /* Set up matrix a and double vectors */
437 for(int i=0; i<n; i++) d->a[i] = d->_data + i*m;
438 d->b = d->_data + n*m;
439 d->x = d->_data + n*m + m;
440 d->w = d->_data + n*m + m + n;
441 d->zz = d->_data + n*m + m + n + n;
442#ifdef TEST_BUFOVERFLOW
443 d->b++; d->x+=2; d->w+=3; d->zz+=4;
444#endif
445
446 /* Allocate memory for integer array */
447#ifdef TEST_BUFOVERFLOW
448 d->index=(int*)malloc(sizeof(int)*(1+n));
449 d->index[n] = 999;
450#else
451 d->index=(int*)malloc(sizeof(int)*n);
452#endif
453 if(d->index==NULL) {free(d->_data); free(d->a); return(TPCERROR_OUT_OF_MEMORY);}
454
455 /* Set data sizes */
456 d->n=n;
457 d->m=m;
458
459 return(TPCERROR_OK);
460}
void nnlsqDataFree(NNLSQDATA *d)
Definition nnlsq.c:378
double * _data
Definition tpclinopt.h:134
@ TPCERROR_FAIL
General error.
@ TPCERROR_OUT_OF_MEMORY
Cannot allocate memory.
@ TPCERROR_OK
No error.

◆ nnlsqDataFree()

void nnlsqDataFree ( NNLSQDATA * d)

Free memory allocated for NNLSQDATA data. All contents are destroyed.

Precondition
Before first use initialize the structure with nnlsqDataInit().
See also
nnlsqDataInit, nnlsqDataAllocate
Author
Vesa Oikonen
Parameters
dPointer to structure.

Definition at line 378 of file nnlsq.c.

381 {
382 if(d==NULL) return;
383 if(d->n<1) return;
384 if(d->m<1) return;
385
386#ifdef TEST_BUFOVERFLOW
387 fprintf(stderr, "testing if buffer overflow happened\n"); fflush(stderr);
388 if(d->index[d->n]!=999) fprintf(stderr, "BUFFER OVERFLOW 1\n");
389 if(!isnan(d->_data[d->m*d->n])) fprintf(stderr, "BUFFER OVERFLOW 2\n");
390 if(!isnan(d->b[d->m])) fprintf(stderr, "BUFFER OVERFLOW 3\n");
391 if(!isnan(d->x[d->n])) fprintf(stderr, "BUFFER OVERFLOW 4\n");
392 if(!isnan(d->w[d->n])) fprintf(stderr, "BUFFER OVERFLOW 5\n");
393 if(!isnan(d->zz[d->m])) fprintf(stderr, "BUFFER OVERFLOW 6\n");
394 fprintf(stderr, "tested buffer overflow\n"); fflush(stderr);
395#endif
396
397 free(d->a);
398 free(d->index);
399 free(d->_data);
400 // then set everything to zero or NULL again
401 nnlsqDataInit(d);
402}
void nnlsqDataInit(NNLSQDATA *d)
Definition nnlsq.c:357

Referenced by nnlsqDataAllocate().

◆ nnlsqDataInit()

void nnlsqDataInit ( NNLSQDATA * d)

Initiate the NNLSQDATA structure before any use.

See also
nnlsqDataFree, nnlsqDataAllocate, nnlsq, nnlsqWght
Author
Vesa Oikonen
Parameters
dPointer to structure to initiate before use.

Definition at line 357 of file nnlsq.c.

360 {
361 if(d==NULL) return;
362 d->n = d->m = 0;
363 d->a = NULL;
364 d->b = d->x = d->w = d->zz = d->_data = NULL;
365 d->index = NULL;
366 d->rnorm = 0.0;
367 d->iternr = 0;
368 d->depf = 0.01; // default
369}

Referenced by nnlsqDataFree().

◆ nnlsqWght()

int nnlsqWght ( NNLSQDATA * d,
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
nnlsqWghtSquared, nnlsq, llsqWght
Parameters
dPointer to filled data structure.
weightWeights for each sample (array of length M).

Definition at line 470 of file nnlsq.c.

475 {
476 if(d==NULL || d->n<1 || d->m<1 || d->a==NULL || d->b==NULL || weight==NULL) return(1);
477
478 /* Check that weights are not zero and get the square roots of them to w[] */
479 double w[d->m];
480 for(int mi=0; mi<d->m; mi++) {
481 if(weight[mi]<=1.0e-20) w[mi]=0.0;
482 else w[mi]=sqrt(weight[mi]);
483 }
484
485 /* Multiply rows of matrix A and elements of vector b with weights*/
486 for(int mi=0; mi<d->m; mi++) {
487 for(int ni=0; ni<d->n; ni++) {
488 d->a[ni][mi]*=w[mi];
489 }
490 d->b[mi]*=w[mi];
491 }
492
493 return(0);
494}

◆ nnlsqWghtSquared()

int nnlsqWghtSquared ( NNLSQDATA * d,
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
nnlsqWght, nnlsq
Parameters
dPointer to filled data structure.
sweightSquared weights for each sample (array of length d->m).

Definition at line 506 of file nnlsq.c.

511 {
512 if(d==NULL || d->n<1 || d->m<1 || d->a==NULL || d->b==NULL || sweight==NULL) return(1);
513
514 /* Multiply rows of matrix A and elements of vector b with weights */
515 for(int mi=0; mi<d->m; mi++) {
516 for(int ni=0; ni<d->n; ni++) {
517 d->a[ni][mi]*=sweight[mi];
518 }
519 d->b[mi]*=sweight[mi];
520 }
521
522 return(0);
523}