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/*****************************************************************************/
62 NNLSQDATA *d,
64 int verbose
65) {
66 if(verbose>0) {printf("%s()\n", __func__); fflush(stdout);}
67
68 /* Check the data */
69 if(d==NULL || d->m<1 || d->n<1 || d->a==NULL || d->b==NULL ||
70 d->x==NULL || d->w==NULL || d->zz==NULL || d->index==NULL)
71 return(2);
72 if(!(d->depf>=1.0E-08) || !(d->depf<0.5)) return(3);
73
74 /* Initialize */
75 for(int ni=0; ni<d->n; ni++) d->x[ni]=0.0;
76 for(int ni=0; ni<d->n; ni++) d->index[ni]=ni;
77 d->rnorm=nan("");
78 int iz1=0;
79 int iz2=d->n-1;
80 int nsetp=0;
81 int npp1=0;
82
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 itermax, iter=0;
88 if(d->iternr>=3) itermax=d->iternr; else itermax=3*d->n;
89 int j=0, jj=0;
90 while(iz1<=iz2 && nsetp<d->m) {
91 /* Compute components of the dual (negative gradient) vector W[] */
92 for(int iz=iz1; iz<=iz2; iz++) {
93 int ni=d->index[iz];
94 double sm=0.;
95 for(int mi=npp1; mi<d->m; mi++) sm+=d->a[ni][mi]*d->b[mi];
96 d->w[ni]=sm;
97 }
98
99 double wmax;
100 int izmax=0;
101 while(1) {
102
103 /* Find largest positive W */
104 wmax=0.0;
105 for(int iz=iz1; iz<=iz2; iz++) {
106 int i=d->index[iz];
107 if(d->w[i]>wmax) {wmax=d->w[i]; izmax=iz;}
108 }
109
110 /* Terminate if wmax<=0.; it indicates satisfaction of the Kuhn-Tucker conditions */
111 if(wmax<=0.0) break;
112
113 /* The sign of W[j] is ok for j to be moved to set P.
114 Begin the transformation and check new diagonal element to avoid near linear dependence. */
115 j=d->index[izmax];
116 double asave=d->a[j][npp1];
117// up=0.0;
118 if(nnlsq_lss_h12(1, npp1, npp1+1, d->m, d->a[j], &up, NULL)) return(2);
119 double unorm=0.0;
120 if(nsetp!=0) for(int mi=0; mi<nsetp; mi++) unorm+=d->a[j][mi]*d->a[j][mi];
121 unorm=sqrt(unorm);
122 double e=unorm+fabs(d->a[j][npp1])*d->depf;
123 if((e-unorm)>0.0) {
124 /* Col j is sufficiently independent. Copy B into ZZ, update ZZ
125 and solve for ztest ( = proposed new value for X[j] ) */
126 for(int mi=0; mi<d->m; mi++) d->zz[mi]=d->b[mi];
127 nnlsq_lss_h12(2, npp1, npp1+1, d->m, d->a[j], &up, d->zz);
128 double ztest=d->zz[npp1]/d->a[j][npp1];
129 /* See if ztest is positive */
130 if(ztest>0.) break;
131 }
132
133 /* Reject j as a candidate to be moved from set Z to set P. Restore
134 A[npp1,j], set W[j]=0., and loop back to test dual coefficients again */
135 d->a[j][npp1]=asave; d->w[j]=0.;
136 } /* while(1) */
137 if(wmax<=0.0) break;
138
139 /* Index j=INDEX[izmax] has been selected to be moved from set Z to set P.
140 Update B and indices, apply householder transformations to cols in
141 new set Z, zero sub-diagonal elements in col j, set W[j]=0. */
142 for(int mi=0; mi<d->m; mi++) d->b[mi]=d->zz[mi];
143 d->index[izmax]=d->index[iz1]; d->index[iz1]=j; iz1++; nsetp=npp1+1; npp1++;
144 if(iz1<=iz2)
145 for(int jz=iz1; jz<=iz2; jz++) {
146 jj=d->index[jz];
147 nnlsq_lss_h12(2, nsetp-1, npp1, d->m, d->a[j], &up, d->a[jj]);
148 }
149 if(nsetp!=d->m) for(int mi=npp1; mi<d->m; mi++) d->a[j][mi]=0.;
150 d->w[j]=0.;
151
152 /* Solve the triangular system; store the solution temporarily in Z[] */
153 for(int mi=0; mi<nsetp; mi++) {
154 int ip=nsetp-(mi+1);
155 if(mi!=0) for(int ii=0; ii<=ip; ii++) d->zz[ii]-=d->a[jj][ii]*d->zz[ip+1];
156 jj=d->index[ip]; d->zz[ip]/=d->a[jj][ip];
157 }
158
159 /* Secondary loop begins here */
160 while(++iter<itermax) {
161 /* See if all new constrained coefficients are feasible; if not, compute alpha */
162 double alpha=2.0;
163 for(int ip=0; ip<nsetp; ip++) {
164 int ni=d->index[ip];
165 if(d->zz[ip]<=0.) {
166 double t=-d->x[ni]/(d->zz[ip]-d->x[ni]);
167 if(alpha>t) {alpha=t; jj=ip-1;}
168 }
169 }
170
171 /* If all new constrained coefficients are feasible then still alpha==2.
172 If so, then exit from the secondary loop to main loop */
173 if(alpha==2.0) break;
174
175 /* Use alpha (0.<alpha<1.) to interpolate between old X and new ZZ */
176 for(int ip=0; ip<nsetp; ip++) {
177 int ni=d->index[ip]; d->x[ni]+=alpha*(d->zz[ip]-d->x[ni]);
178 }
179
180 /* Modify A and B and the INDEX arrays to move coefficient i from set P to set Z. */
181 int pfeas=1;
182 int k=d->index[jj+1];
183 do {
184 d->x[k]=0.;
185 if(jj!=(nsetp-1)) {
186 jj++;
187 for(int ni=jj+1; ni<nsetp; ni++) {
188 int ii=d->index[ni]; d->index[ni-1]=ii;
189 double ss, cc;
190 nnlsq_lss_g1(d->a[ii][ni-1], d->a[ii][ni], &cc, &ss, &d->a[ii][ni-1]);
191 d->a[ii][ni]=0.0;
192 for(int nj=0; nj<d->n; nj++) if(nj!=ii) {
193 /* Apply procedure G2 (CC,SS,A(J-1,L),A(J,L)) */
194 double temp=d->a[nj][ni-1];
195 d->a[nj][ni-1]=cc*temp+ss*d->a[nj][ni];
196 d->a[nj][ni]=-ss*temp+cc*d->a[nj][ni];
197 }
198 /* Apply procedure G2 (CC,SS,B(J-1),B(J)) */
199 double temp=d->b[ni-1];
200 d->b[ni-1]=cc*temp+ss*d->b[ni];
201 d->b[ni]=-ss*temp+cc*d->b[ni];
202 }
203 }
204 npp1=nsetp-1; nsetp--; iz1--; d->index[iz1]=k;
205
206 /* See if the remaining coefficients in set P are feasible; they should be because of
207 the way alpha was determined. If any are infeasible it is due to round-off error.
208 Any that are non-positive will be set to zero and moved from set P to set Z. */
209 pfeas=1;
210 for(jj=0; jj<nsetp; jj++) {
211 k=d->index[jj]; if(d->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<d->m; mi++) d->zz[mi]=d->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++) d->zz[ii]-=d->a[jj][ii]*d->zz[ip+1];
220 jj=d->index[ip]; d->zz[ip]/=d->a[jj][ip];
221 }
222 } /* end of secondary loop */
223
224 if(iter>=itermax) break;
225 for(int ip=0; ip<nsetp; ip++) {int k=d->index[ip]; d->x[k]=d->zz[ip];}
226 } /* end of main loop */
227
228 if(npp1>=d->m) for(int ni=0; ni<d->n; ni++) d->w[ni]=0.;
229
230 /* Compute the norm of the final residual vector (sum-of-squares) */
231 d->rnorm=0.0;
232 for(int mi=npp1; mi<d->m; mi++) d->rnorm+=(d->b[mi]*d->b[mi]);
233
234 d->iternr=iter;
235 if(verbose>2) printf(" %d iterations.\n", iter);
236 if(iter>=itermax) {
237 if(verbose>1) printf(" max iterations reached.\n");
238 return(1);
239 }
240 return(0);
241} /* nnlsq */
242/*****************************************************************************/
243
244/*****************************************************************************/
246
250int nnlsq_lss_h12(
253 int mode,
255 int lpivot,
257 int l1,
259 int m,
265 double *u,
268 double *up,
272 double *cm
273) {
274 /* Check parameters */
275 if(mode!=1 && mode!=2) return(1);
276 if(u==NULL || up==NULL) return(2);
277 if(lpivot<0 || l1<0 || m<0) return(3);
278 if(lpivot>=m || lpivot>=l1 || l1>m) return(4);
279
280 double cl = fabs(u[lpivot]);
281 if(mode==2 && cl<=0.) return(0);
282
283 if(mode==1) { /* Construct the transformation */
284 /* trying to compensate overflow */
285 for(int j=l1; j<m; j++) { // finding maximum
286 cl = fmax(fabs(u[j]), cl);
287 }
288 // zero vector?
289 if(cl<=0.) return(0);
290
291 double clinv=1.0/cl;
292 // cl = sqrt( (u[pivot]*clinv)^2 + sigma(i=l1..m)( (u[i]*clinv)^2 ) )
293 double d1=u[lpivot]*clinv;
294 double sm=d1*d1;
295 for(int j=l1; j<m; j++) {
296 double d2=u[j]*clinv;
297 sm+=d2*d2;
298 }
299 cl*=sqrt(sm);
300 if(u[lpivot] > 0.) cl=-cl;
301 *up = u[lpivot] - cl;
302 u[lpivot]=cl;
303 }
304
305 // no vectors where to apply? only change pivot vector!
306 double b=(*up)*u[lpivot];
307
308 /* b must be non-positive here; if b>=0., then return */
309 if(b>=0.0) return(0); // was if(b==0) before 2013-06-22
310
311 // Transform the cm vector, if requested
312 if(cm!=NULL) {
313 double sm = cm[lpivot] * (*up);
314 for(int k=l1; k<m; k++) sm += cm[k] * u[k];
315 if(sm!=0.0) {
316 sm *= (1.0/b);
317 cm[lpivot] += sm*(*up);
318 for(int k=l1; k<m; k++) cm[k] += u[k]*sm;
319 }
320 }
321
322 return(0);
323} /* nnlsq_lss_h12 */
324/*****************************************************************************/
325
326/*****************************************************************************/
334void nnlsq_lss_g1(double a, double b, double *cterm, double *sterm, double *sig)
335{
336 double d1, xr, yr;
337
338 if(fabs(a)>fabs(b)) {
339 xr=b/a; d1=xr; yr=hypot(d1, 1.0); d1=1./yr;
340 *cterm=copysign(d1, a);
341 *sterm=(*cterm)*xr; *sig=fabs(a)*yr;
342 } else if(b!=0.) {
343 xr=a/b; d1=xr; yr=hypot(d1, 1.0); d1=1./yr;
344 *sterm=copysign(d1, b);
345 *cterm=(*sterm)*xr; *sig=fabs(b)*yr;
346 } else {
347 *sig=0.; *cterm=0.; *sterm=1.;
348 }
349} /* nnlsq_lss_g1 */
351/*****************************************************************************/
352
353/*****************************************************************************/
360 NNLSQDATA *d
361) {
362 if(d==NULL) return;
363 d->n = d->m = 0;
364 d->a = NULL;
365 d->b = d->x = d->w = d->zz = d->_data = NULL;
366 d->index = NULL;
367 d->rnorm = 0.0;
368 d->iternr = 0;
369 d->depf = 0.01; // default
370}
371/*****************************************************************************/
372
373/*****************************************************************************/
381 NNLSQDATA *d
382) {
383 if(d==NULL) return;
384 if(d->n<1) return;
385 if(d->m<1) return;
386
387#ifdef TEST_BUFOVERFLOW
388 fprintf(stderr, "testing if buffer overflow happened\n"); fflush(stderr);
389 if(d->index[d->n]!=999) fprintf(stderr, "BUFFER OVERFLOW 1\n");
390 if(!isnan(d->_data[d->m*d->n])) fprintf(stderr, "BUFFER OVERFLOW 2\n");
391 if(!isnan(d->b[d->m])) fprintf(stderr, "BUFFER OVERFLOW 3\n");
392 if(!isnan(d->x[d->n])) fprintf(stderr, "BUFFER OVERFLOW 4\n");
393 if(!isnan(d->w[d->n])) fprintf(stderr, "BUFFER OVERFLOW 5\n");
394 if(!isnan(d->zz[d->m])) fprintf(stderr, "BUFFER OVERFLOW 6\n");
395 fprintf(stderr, "tested buffer overflow\n"); fflush(stderr);
396#endif
397
398 free(d->a);
399 free(d->index);
400 free(d->_data);
401 // then set everything to zero or NULL again
402 nnlsqDataInit(d);
403}
404/*****************************************************************************/
405
406/*****************************************************************************/
414 NNLSQDATA *d,
416 const int n,
418 const int m
419) {
420 if(d==NULL) return(TPCERROR_FAIL);
421 /* Delete any previous contents */
422 nnlsqDataFree(d);
423 /* If no memory is requested, then return fail */
424 if(n<1 || m<1) return(TPCERROR_FAIL);
425
426 /* Allocate memory for all double arrays and matrix */
427 int s = n*m + m + n + n + m;
428#ifdef TEST_BUFOVERFLOW
429 s+=5;
430#endif
431 d->_data=(double*)malloc(sizeof(double)*s);
432 if(d->_data==NULL) return(TPCERROR_OUT_OF_MEMORY);
433 for(int i=0; i<s; i++) d->_data[i]=nan("");
434 /* Allocate memory for matrix pointers */
435 d->a=(double**)malloc(sizeof(double*)*n);
436 if(d->a==NULL) {free(d->_data); return(TPCERROR_OUT_OF_MEMORY);}
437 /* Set up matrix a and double vectors */
438 for(int i=0; i<n; i++) d->a[i] = d->_data + i*m;
439 d->b = d->_data + n*m;
440 d->x = d->_data + n*m + m;
441 d->w = d->_data + n*m + m + n;
442 d->zz = d->_data + n*m + m + n + n;
443#ifdef TEST_BUFOVERFLOW
444 d->b++; d->x+=2; d->w+=3; d->zz+=4;
445#endif
446
447 /* Allocate memory for integer array */
448#ifdef TEST_BUFOVERFLOW
449 d->index=(int*)malloc(sizeof(int)*(1+n));
450 d->index[n] = 999;
451#else
452 d->index=(int*)malloc(sizeof(int)*n);
453#endif
454 if(d->index==NULL) {free(d->_data); free(d->a); return(TPCERROR_OUT_OF_MEMORY);}
455
456 /* Set data sizes */
457 d->n=n;
458 d->m=m;
459
460 return(TPCERROR_OK);
461}
462/*****************************************************************************/
463
464/*****************************************************************************/
472 NNLSQDATA *d1,
474 NNLSQDATA *d2
475) {
476 if(d1==NULL || d2==NULL || d1==d2) return(TPCERROR_FAIL);
477 if(d1->n<1 || d1->m<1) return(2);
478 /* If data sizes are different then allocate memory for target */
479 if(d1->n!=d2->n || d1->m!=d2->m) {
480 nnlsqDataFree(d2);
481 int ret=nnlsqDataAllocate(d2, d1->n, d1->m);
482 if(ret!=TPCERROR_OK) return(ret);
483 }
484 /* Copy contents */
485 for(int ni=0; ni<d1->n; ni++)
486 for(int mi=0; mi<d1->m; mi++)
487 d2->a[ni][mi]=d1->a[ni][mi];
488 for(int mi=0; mi<d1->m; mi++)
489 d2->b[mi]=d1->b[mi];
490 for(int ni=0; ni<d1->n; ni++)
491 d2->x[ni]=d1->x[ni];
492 for(int ni=0; ni<d1->n; ni++)
493 d2->w[ni]=d1->w[ni];
494 for(int mi=0; mi<d1->m; mi++)
495 d2->zz[mi]=d1->zz[mi];
496 for(int ni=0; ni<d1->n; ni++)
497 d2->index[ni]=d1->index[ni];
498 d2->rnorm=d1->rnorm;
499 d2->iternr=d1->iternr;
500 d2->depf=d1->depf;
501
502 return(TPCERROR_OK);
503}
504/*****************************************************************************/
505
506/*****************************************************************************/
515 NNLSQDATA *d,
517 double *weight
518) {
519 if(d==NULL || d->n<1 || d->m<1 || d->a==NULL || d->b==NULL || weight==NULL) return(1);
520
521 /* Check that weights are not zero and get the square roots of them to w[] */
522 double w[d->m];
523 for(int mi=0; mi<d->m; mi++) {
524 if(weight[mi]<=1.0e-20) w[mi]=0.0;
525 else w[mi]=sqrt(weight[mi]);
526 }
527
528 /* Multiply rows of matrix A and elements of vector b with weights*/
529 for(int mi=0; mi<d->m; mi++) {
530 for(int ni=0; ni<d->n; ni++) {
531 d->a[ni][mi]*=w[mi];
532 }
533 d->b[mi]*=w[mi];
534 }
535
536 return(0);
537}
538/*****************************************************************************/
539
540/*****************************************************************************/
551 NNLSQDATA *d,
553 double *sweight
554) {
555 if(d==NULL || d->n<1 || d->m<1 || d->a==NULL || d->b==NULL || sweight==NULL) return(1);
556
557 /* Multiply rows of matrix A and elements of vector b with weights */
558 for(int mi=0; mi<d->m; mi++) {
559 for(int ni=0; ni<d->n; ni++) {
560 d->a[ni][mi]*=sweight[mi];
561 }
562 d->b[mi]*=sweight[mi];
563 }
564
565 return(0);
566}
567/*****************************************************************************/
568
569/*****************************************************************************/
int nnlsqDataWghtSquared(NNLSQDATA *d, double *sweight)
Definition nnlsq.c:549
int nnlsqDataWght(NNLSQDATA *d, double *weight)
Definition nnlsq.c:513
void nnlsqDataFree(NNLSQDATA *d)
Definition nnlsq.c:379
int nnlsqDataAllocate(NNLSQDATA *d, const int n, const int m)
Definition nnlsq.c:412
void nnlsqDataInit(NNLSQDATA *d)
Definition nnlsq.c:358
int nnlsqDataCopy(NNLSQDATA *d1, NNLSQDATA *d2)
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.