TPCCLIB
Loading...
Searching...
No Matches
nnlsq.c
Go to the documentation of this file.
1
12/*****************************************************************************/
13#include "tpcclibConfig.h"
14/*****************************************************************************/
15#include <stdio.h>
16#include <stdlib.h>
17#include <math.h>
18/*****************************************************************************/
19#include "tpcextensions.h"
20/*****************************************************************************/
21#include "tpclinopt.h"
22/*****************************************************************************/
23
24/*****************************************************************************/
25// Comment this out to NOT test for buffer overflow
26//#define TEST_BUFOVERFLOW 1
27/*****************************************************************************/
28
29/*****************************************************************************/
31/* Local function definitions */
32int nnlsq_lss_h12(int mode, int lpivot, int l1, int m, double *u, double *up, double *cm);
33void nnlsq_lss_g1(double a, double b, double *cterm, double *sterm, double *sig);
35/*****************************************************************************/
36
37/*****************************************************************************/
61 NNLSQDATA *d,
63 int verbose
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 */
241/*****************************************************************************/
242
243/*****************************************************************************/
245
249int nnlsq_lss_h12(
252 int mode,
254 int lpivot,
256 int l1,
258 int m,
264 double *u,
267 double *up,
271 double *cm
272) {
273 /* Check parameters */
274 if(mode!=1 && mode!=2) return(1);
275 if(u==NULL || up==NULL) return(2);
276 if(lpivot<0 || l1<0 || m<0) return(3);
277 if(lpivot>=m || lpivot>=l1 || l1>m) return(4);
278
279 double cl = fabs(u[lpivot]);
280 if(mode==2 && cl<=0.) return(0);
281
282 if(mode==1) { /* Construct the transformation */
283 /* trying to compensate overflow */
284 for(int j=l1; j<m; j++) { // finding maximum
285 cl = fmax(fabs(u[j]), cl);
286 }
287 // zero vector?
288 if(cl<=0.) return(0);
289
290 double clinv=1.0/cl;
291 // cl = sqrt( (u[pivot]*clinv)^2 + sigma(i=l1..m)( (u[i]*clinv)^2 ) )
292 double d1=u[lpivot]*clinv;
293 double sm=d1*d1;
294 for(int j=l1; j<m; j++) {
295 double d2=u[j]*clinv;
296 sm+=d2*d2;
297 }
298 cl*=sqrt(sm);
299 if(u[lpivot] > 0.) cl=-cl;
300 *up = u[lpivot] - cl;
301 u[lpivot]=cl;
302 }
303
304 // no vectors where to apply? only change pivot vector!
305 double b=(*up)*u[lpivot];
306
307 /* b must be non-positive here; if b>=0., then return */
308 if(b>=0.0) return(0); // was if(b==0) before 2013-06-22
309
310 // Transform the cm vector, if requested
311 if(cm!=NULL) {
312 double sm = cm[lpivot] * (*up);
313 for(int k=l1; k<m; k++) sm += cm[k] * u[k];
314 if(sm!=0.0) {
315 sm *= (1.0/b);
316 cm[lpivot] += sm*(*up);
317 for(int k=l1; k<m; k++) cm[k] += u[k]*sm;
318 }
319 }
320
321 return(0);
322} /* nnlsq_lss_h12 */
323/*****************************************************************************/
324
325/*****************************************************************************/
333void nnlsq_lss_g1(double a, double b, double *cterm, double *sterm, double *sig)
334{
335 double d1, xr, yr;
336
337 if(fabs(a)>fabs(b)) {
338 xr=b/a; d1=xr; yr=hypot(d1, 1.0); d1=1./yr;
339 *cterm=copysign(d1, a);
340 *sterm=(*cterm)*xr; *sig=fabs(a)*yr;
341 } else if(b!=0.) {
342 xr=a/b; d1=xr; yr=hypot(d1, 1.0); d1=1./yr;
343 *sterm=copysign(d1, b);
344 *cterm=(*sterm)*xr; *sig=fabs(b)*yr;
345 } else {
346 *sig=0.; *cterm=0.; *sterm=1.;
347 }
348} /* nnlsq_lss_g1 */
350/*****************************************************************************/
351
352/*****************************************************************************/
359 NNLSQDATA *d
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}
370/*****************************************************************************/
371
372/*****************************************************************************/
380 NNLSQDATA *d
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}
403/*****************************************************************************/
404
405/*****************************************************************************/
413 NNLSQDATA *d,
415 const int n,
417 const int m
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}
461/*****************************************************************************/
462
463/*****************************************************************************/
472 NNLSQDATA *d,
474 double *weight
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}
495/*****************************************************************************/
496
497/*****************************************************************************/
508 NNLSQDATA *d,
510 double *sweight
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}
524/*****************************************************************************/
525
526/*****************************************************************************/
void nnlsqDataFree(NNLSQDATA *d)
Definition nnlsq.c:378
int nnlsqDataAllocate(NNLSQDATA *d, const int n, const int m)
Definition nnlsq.c:411
void nnlsqDataInit(NNLSQDATA *d)
Definition nnlsq.c:357
int nnlsqWghtSquared(NNLSQDATA *d, double *sweight)
Definition nnlsq.c:506
int nnlsqWght(NNLSQDATA *d, double *weight)
Definition nnlsq.c:470
int nnlsq(NNLSQDATA *d, int verbose)
Definition nnlsq.c:55
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 * _data
Definition tpclinopt.h:134
double * zz
Definition tpclinopt.h:129
double depf
Definition tpclinopt.h:141
double * w
Definition tpclinopt.h:127
Header file for library libtpcextensions.
@ TPCERROR_FAIL
General error.
@ TPCERROR_OUT_OF_MEMORY
Cannot allocate memory.
@ TPCERROR_OK
No error.
Header file for libtpclinopt.