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

BVLS (Bounded-value least-squares). More...

#include "libtpcmodel.h"

Go to the source code of this file.

Functions

int bvls (int key, const int m, const int n, double *a, double *b, double *bl, double *bu, double *x, double *w, double *act, double *zz, int *istate, int *iter, int verbose)
 Bounded-value least-squares method to solve the linear problem A x ~ b , subject to limit1 <= x <= limit2.
 
int llsqWght (int N, int M, double **A, double *a, double *b, double *weight)
 
int llsqWghtSquared (int N, int M, double **A, double *a, double *b, double *sweight)
 

Detailed Description

BVLS (Bounded-value least-squares).

Author
Vesa Oikonen

Function bvls() is dependent on function qrLH() in qrlsq.c.

Definition in file bvls.c.

Function Documentation

◆ bvls()

int bvls ( int key,
const int m,
const int n,
double * a,
double * b,
double * bl,
double * bu,
double * x,
double * w,
double * act,
double * zz,
int * istate,
int * iter,
int verbose )

Bounded-value least-squares method to solve the linear problem A x ~ b , subject to limit1 <= x <= limit2.

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, and Fortran codes by R.L. Parker and P.B. Stark, and by J. Burkardt.

See also
nnls, qrLH, llsqWghtSquared, llsqWght
Returns
Returns 0 when successful, -1 if iteration count exceeded the limit, 1 in case of invalid data or settings, and 2 if problem cannot be solved.
Parameters
keyEnter 0 to solve the problem from scratch, or <>0 to initialize the routine using caller's guess about which parameters are active within their bounds, which are at their lower bounds, and which at their upper bounds, as supplied in the array istate ('warm start'). When key <> 0, the routine initially sets the active components to the averages of their upper and lower bounds.
mNumber of samples in matrix A and the length of vector b.
nNumber of parameters in matrix A and the length of vector x. The n must not be > m unless at least n-m variables are non-active (set to their bounds)..
aPointer to matrix A; matrix must be given as an n*m array, containing n consecutive m-length vectors. Contents of A are modified in this routine.
bPointer to vector b of length m. Contents of b are modified in this routine.
blArray BL[0..n-1] of lower bounds for parameters.
buArray BU[0..n-1] of upper bounds for parameters.
xPointer to the result vector x of length n.
wPointer to an array of length n, which will be used as working memory, and the minimum 2-norm || a.x-b ||, (R^2), will be written in w[0].
actPointer to an array of length m*(n+2), or m*(m+2) if m<n, which will be used as working memory.
zzPointer to an array of length m, to be used as working memory.
istatePointer to an integer array of length n+1. If parameter key <>0, then the last position istate[n] must contain the total number of components at their bounds (nbound, the ‘bound variables’). The absolute values of the first nbound entries of istate[] are the indices of these ‘bound’ components of x[]. The sign of istate[0.. nbound-1] entries indicates whether x[|istate[i]|] is at its upper (positive) or lower (negative) bound. Entries istate[nbound..n-1] contain the indices of the active components.
iterNumber of performed iterations is returned in this variable. Maximum nr of iterations is set with this variable, or set it to 0 to use the default maximum, 3*n.
verboseVerbose level; if zero, then nothing is printed to stderr or stdout

Definition at line 24 of file bvls.c.

72 {
73 if(verbose>0) {printf("bvls(%d, %d, %d, ...)\n", key, m, n); fflush(stdout);}
74 /* Check the input */
75 if(a==NULL || b==NULL || bl==NULL || bu==NULL || x==NULL || w==NULL ||
76 act==NULL || zz==NULL || istate==NULL || iter==NULL || n<1 || m<1)
77 {
78 if(verbose>0) fprintf(stderr, "Error: invalid input to BVLS.\n");
79 return(1);
80 }
81
82 int maxIter=*iter; if(maxIter<3) maxIter=3*n;
83 *iter=0;
84 const double eps=1.0E-13; // stopping rule
85
86 /* Step 1. Initialize everything -- active and bound sets, initial values, etc. */
87 if(verbose>1) {printf("step 1\n"); fflush(stdout);}
88
89 /* Set mm to the smaller of matrix dimensions n and m. */
90 int mm; if(m<n) mm=m; else mm=n;
91 int aindx=0; // one-based index of X[] that determined the alpha; zero means not set.
92 int aindxsign=0; // +1 if zz[aindx]> bu[aindx], -1 if zz[aindx]<bl[aindx]
93 /* istateFromStep5 is the one-based index of the parameter that most wants to be active;
94 zero value means its is currently not set. */
95 int istateFromStep5 = 0;
96
97 /* Check the consistency of given bounds bl[] and bu[]. */
98 {
99 double maxrange=0.0;
100 for(int ni=0; ni<n; ni++) {
101 double d=bu[ni]-bl[ni];
102 if(verbose>3) printf(" bounds[%d]: %g %g\n", 1+ni, bl[ni], bu[ni]);
103 if(d<0.0) {
104 if(verbose>0) fprintf(stderr, "Error: inconsistent bounds in BVLS.\n");
105 return(1);
106 }
107 maxrange=fmax(maxrange, d);
108 }
109 if(verbose>2) printf(" maxrange := %g\n", maxrange);
110 if(maxrange<1.0E-10) {
111 if(verbose>0) fprintf(stderr, "Error: no free variables in BVLS.\n");
112 return(1);
113 }
114 }
115
116 /* In a fresh initialization (key=0), bind all variables at their lower bounds.
117 If key<>0, use the supplied istate[] array to initialize the variables. */
118 int nbound, nact; // number of bound and active parameters, respectively.
119 if(key==0) {
120 nbound=n;
121 /* Write the indices; negative sign indicates lower limit */
122 /* Note that these indices must start from 1, because 0 can not have sign */
123 for(int ni=0; ni<nbound; ni++) istate[ni]=-(1+ni);
124 } else {
125 nbound=istate[n];
126 }
127 nact=n-nbound;
128 if(nact>mm) {
129 if(verbose>0) fprintf(stderr, "Error: too many active variables in BVLS starting solution.\n");
130 return(2);
131 }
132 for(int ni=0; ni<nbound; ni++) {
133 int i=abs(istate[ni])-1;
134 if(istate[ni]<0) x[i]=bl[i]; else x[i]=bu[i];
135 }
136
137 /* In a warm start (key<>0, and nbound<n) initialize the active variables to the mean of
138 their bounds. This is needed in case the initial QR results in active variables
139 out-of-bounds and Steps 8-11 get executed the first time through. */
140 for(int ni=nbound; ni<n; ni++) {
141 int i=abs(istate[ni])-1; // indices of active variables should not have signs, but let's be sure
142 x[i]=0.5*(bl[i]+bu[i]);
143 }
144
145 /* Compute bnorm, the norm of the data vector b, for reference. */
146 double bnorm=0.0;
147 for(int mi=0; mi<m; mi++) bnorm+=b[mi]*b[mi];
148 bnorm=sqrt(bnorm); if(verbose>2) printf(" initial_bnorm := %g\n", bnorm);
149
150
151 /*
152 * Main loop
153 */
154 int skipStep2=0;
155 double obj=0.0;
156 int iact=0; // The component x[iact] is the one that most wants to become active
157
158 for(*iter=1; *iter<=maxIter; ++(*iter)) {
159
160 if(verbose>1) {printf("iteration %d\n", *iter); fflush(stdout);}
161
162 if(!skipStep2) {
163 /* Step 2. */ if(verbose>1) {printf(" step 2\n"); fflush(stdout);}
164
165 /* Initialize the negative gradient vector w(*). */
166 for(int ni=0; ni<n; ni++) w[ni]=0.0;
167
168 /* Compute the residual vector b-a.x , the negative gradient vector w[*], and
169 the current objective value obj = || a.x - b ||.
170 The residual vector is stored in the mm+1'st column of act[*,*]. */
171 obj=0.0;
172 for(int mi=0; mi<m; mi++) {
173 double ri=b[mi];
174 for(int ni=0; ni<n; ni++) ri-=a[mi+ni*m]*x[ni];
175 obj+=ri*ri;
176 for(int ni=0; ni<n; ni++) w[ni]+=a[mi+ni*m]*ri;
177 act[mi+mm*m]=ri;
178 }
179 if(verbose>3) {printf(" obj := %g\n", obj); fflush(stdout);}
180
181 /* Converged? Stop if the misfit << || b ||, or if all components are active
182 (unless this is the first iteration from a 'warm start'). */
183 if((sqrt(obj)<=bnorm*eps) || ((*iter)>1 && nbound==0)) {
184 if(verbose>1) {printf("bvls converged.\n"); fflush(stdout);}
185 istate[n]=nbound;
186 w[0]=sqrt(obj);
187 return(0);
188 }
189
190 /* Add the contribution of the active components back into the residual. */
191 for(int ni=nbound; ni<n; ni++) {
192 int i=abs(istate[ni])-1;
193 for(int mi=0; mi<m; mi++) act[mi+mm*m] += a[mi+i*m]*x[i];
194 }
195 if(verbose>9) {
196 printf("Residual vector:\n");
197 for(int mi=0; mi<m; mi++) printf("\t%g", act[mi+mm*m]);
198 printf("\n");
199 }
200
201 }
202
203
204
205 /* The first iteration in a 'warm start' requires immediate QR in Step 6
206 but mostly we want go through Steps 3-5 first . */
207 if(key!=0 && (*iter)==1) {
208 if(verbose>1) printf(" 'warm start' requires immediate QR in Step 6\n");
209 } else {
210
211 int it; // variable indicating the element in istate that most wants to be active
212
213 do {
214 /* Steps 3, 4. */ if(verbose>1) {printf(" steps 3 and 4\n"); fflush(stdout);}
215 /* Find the bound element that most wants to be active. */
216 double worst=0.0;
217 it=1;
218 for(int ni=0; ni<nbound; ni++) {
219 int i=abs(istate[ni])-1;
220 double bad; if(istate[ni] < 0) bad=-w[i]; else bad=+w[i];
221 if(bad < worst) { it=ni+1; worst=bad; iact=i; }
222 }
223
224 /* Test whether the Kuhn-Tucker condition is met. */
225 if(worst>=0.0) {
226 if(verbose>1) {printf("Kuhn-Tucker condition is met.\n"); fflush(stdout);}
227 istate[n]=nbound;
228 w[0]=sqrt(obj);
229 return(0);
230 }
231
232 /* The component x[iact] is the one that most wants to become active.
233 If the last successful change in the active set was to move x[iact] to a bound,
234 don't let x[iact] in now: set the derivative of the misfit with respect to x[iact]
235 to zero and return to the Kuhn-Tucker test. */
236 if(iact==(aindx-1)) w[aindx-1]=0.0;
237 } while(iact==(aindx-1)); // Step 3 again
238
239 /* Step 5. */ if(verbose>1) {printf(" step 5\n"); fflush(stdout);}
240
241 /* Undo the effect of the new (potentially) active variable on the residual vector. */
242 if(istate[it-1]==0) { // remove this if never happening
243 if(verbose>0) fprintf(stderr, "Error: BVLS istate is zero!\n");
244 return(1);
245 }
246 {
247 double bnd;
248 if(istate[it-1]>0) bnd=bu[iact]; else bnd=bl[iact];
249 for(int mi=0; mi<m; mi++) act[mi+mm*m]+=bnd*a[mi+iact*m];
250 }
251
252 /* Set flag istateFromStep5, indicating that Step 6 was entered from Step 5.
253 This forms the basis of a test for instability: the gradient calculation shows that x[iact]
254 wants to join the active set; if QR puts x[iact] beyond the bound from which it came,
255 the gradient calculation was in error and the variable should not have been introduced. */
256 istateFromStep5=istate[it-1];
257
258 /* Swap the indices (in istate) of the new active variable and the rightmost bound variable;
259 `unbind' that location by decrementing nbound. */
260 istate[it-1]=istate[nbound-1];
261 nbound--; nact++;
262 istate[nbound]=1+iact;
263 if(mm<nact) {
264 if(verbose>0) fprintf(stderr, "Error: too many free variables in BVLS.\n");
265 return(2);
266 }
267
268 } // finalized steps 3-5
269
270 do {
271
272 skipStep2=0;
273
274 /* Step 6. */ if(verbose>1) {printf(" step 6\n"); fflush(stdout);}
275
276 /* Load array act with the appropriate columns of A for QR. For added stability, reverse
277 the column ordering so that the most recent addition to the active set is in the last
278 column. Also copy the residual vector from act[., mm] into act[., mm+1]. */
279 for(int mi=0; mi<m; mi++) {
280 act[mi+(mm+1)*m]=act[mi+mm*m]; // vector b for QR
281 for(int ni=nbound; ni<n; ni++) {
282 int i=abs(istate[ni])-1;
283 act[mi+(nact+nbound-ni-1)*m]=a[mi+i*m];
284 }
285 }
286 if(verbose>9) {
287 printf("Matrix A for QR:\n");
288 for(int ni=0; ni<nact; ni++) {
289 for(int mi=0; mi<m; mi++) printf("\t%g", act[mi+ni*nact]);
290 printf("\n");
291 }
292 printf("Vector B for QR:\n");
293 for(int mi=0; mi<m; mi++) printf("\t%g", act[(mm+1)*m + mi]);
294 printf("\n");
295 }
296
297 /* Test for linear dependence in QR, and for an instability that moves the variable
298 just introduced away from the feasible region (rather than into the region or
299 all the way through it). In either case, remove the latest vector introduced from
300 the active set and adjust the residual vector accordingly.
301 Set the gradient component (w[iact]) to zero and return to the Kuhn-Tucker test. */
302 double r2;
303 if(qrLH(m, nact, act, &act[(mm+1)*m], zz, &r2) !=0 ||
304 (istateFromStep5>0 && zz[nact-1]>bu[iact]) ||
305 (istateFromStep5<0 && zz[nact-1]<bl[iact]) )
306 {
307 nbound++;
308 if(bu[iact]>x[iact]) istate[nbound-1]=-istate[nbound-1];
309 nact--;
310 for(int mi=0; mi<m; mi++) act[mi+mm*m]-=x[iact]*a[mi+iact*m];
311 istateFromStep5 = 0; // not from step 5
312 w[iact]=0.0;
313 skipStep2=1; // we want to skip Step 2 and go directly to Step 3
314 if(verbose>3) {printf(" going from step 6 to step 3\n"); fflush(stdout);}
315 break; // go to step 3
316 }
317
318 /* If Step 6 was entered from Step 5 and we are here, a new variable has been successfully
319 introduced into the active set; the last variable that was fixed at a bound is again
320 permitted to become active. */
321 if(istateFromStep5!=0) aindx=0;
322 istateFromStep5=0;
323
324 /* Step 7. */ if(verbose>1) {printf(" step 7\n"); fflush(stdout);}
325 /* Check for strict feasibility of the new QR solution. */
326 int qr_solution_feasible=1;
327 int indexHolder=0;
328 if(verbose>8) printf(" nact=%d nbound=%d\n", nact, nbound);
329 for(int ni=0; ni<nact; ni++) {
330 indexHolder=ni; // Loop in step 8 will start from this
331 int i=abs(istate[ni+nbound])-1;
332 if(verbose>8) {
333 printf(" istate[%d]=%d\n", ni+nbound, 1+i);
334 printf(" zz[%d]=%g bl[%d]=%g bu[%d]=%g\n", nact-ni-1, zz[nact-ni-1], i, bl[i], i, bu[i]);
335 }
336 if(zz[nact-ni-1]<bl[i] || zz[nact-ni-1]>bu[i]) {
337 if(verbose>3) {printf(" new iterate is not feasible\n"); fflush(stdout);}
338 qr_solution_feasible=0; break; // go to Step 8
339 }
340 }
341 if(verbose>8) printf(" indexHolder=%d\n", indexHolder);
342 if(qr_solution_feasible) {
343 if(verbose>3) {printf(" new iterate is feasible\n"); fflush(stdout);}
344 for(int ni=0; ni<nact; ni++) {
345 int i=abs(istate[ni+nbound])-1;
346 x[i]=zz[nact-ni-1];
347 }
348 /* New iterate is feasible; back to the top. */
349 break; // Back to the start of the main loop
350 }
351
352 { // keep local variables alpha and alf local
353 double alpha=2.0;
354 /* Steps 8 and 9 */ if(verbose>1) {printf(" steps 8 and 9\n"); fflush(stdout);}
355 double alf=alpha;
356 for(int ni=indexHolder; ni<nact; ni++) {
357 int i=abs(istate[ni+nbound])-1;
358 if(zz[nact-ni-1] > bu[i]) alf=(bu[i]-x[i])/(zz[nact-ni-1]-x[i]);
359 if(zz[nact-ni-1] < bl[i]) alf=(bl[i]-x[i])/(zz[nact-ni-1]-x[i]);
360 if(alf<alpha) {
361 alpha=alf;
362 aindx=1+i;
363 if((zz[nact-ni-1]-bl[i])<0.0) aindxsign=-1; else aindxsign=+1;
364 }
365 }
366 /* Step 10 */ if(verbose>1) {printf(" step 10\n"); fflush(stdout);}
367 for(int ni=0; ni<nact; ni++) {
368 int i=abs(istate[ni+nbound])-1;
369 x[i]+=alpha*(zz[nact-ni-1]-x[i]);
370 }
371 }
372
373 /* Step 11 */ if(verbose>1) {printf(" step 11\n"); fflush(stdout);}
374 /* Move the variable that determined alpha to the appropriate bound
375 (aindx is its index; sj is + if zz[aindx]> bu[aindx], - if zz[aindx]<bl[aindx] ).
376 If any other component of x is infeasible at this stage, it must be due to round-off.
377 Bind every infeasible component and every component at a bound to the appropriate bound.
378 Correct the residual vector for any variables moved to bounds. Since at least one
379 variable is removed from the active set in this step, Loop B
380 (Steps 6-11) terminates after at most nact steps. */
381 {
382 int noldb=nbound;
383 for(int ni=0; ni<nact; ni++) {
384 int i=abs(istate[ni+noldb])-1;
385 if((bu[i]-x[i]<=0.0) || (i==(aindx-1) && aindxsign>0)) {
386 /* Move x[i] to its upper bound. */
387 x[i]=bu[i];
388 istate[ni+noldb]=istate[nbound]; istate[nbound]=+(1+i); nbound++;
389 for(int mi=0; mi<m; mi++) act[mi+mm*m]-=bu[i]*a[mi+i*m];
390 } else if( ((x[i]-bl[i])<=0.0) || (i==(aindx-1) && aindxsign<0)) {
391 /* Move x(j) to its lower bound. */
392 x[i]=bl[i];
393 istate[ni+noldb]=istate[nbound]; istate[nbound]=-(1+i); nbound++;
394 for(int mi=0; mi<m; mi++) act[mi+mm*m]-=bl[i]*a[mi+i*m];
395 }
396 }
397 nact=n-nbound;
398 }
399 /* If there are still active variables left, repeat the QR; if not, go back to step 6. */
400 } while(nact>0);
401
402 } // main loop
403
404 /* iterMax reached */
405 if(verbose>0) fprintf(stderr, "Error: BVLS fails to converge.\n");
406 return(-1);
407}
int qrLH(const unsigned int m, const unsigned int n, double *a, double *b, double *x, double *r2)
Solve over-determined least-squares problem A x ~ b using successive Householder rotations.
Definition qr.c:633

◆ llsqWght()

int llsqWght ( int N,
int M,
double ** A,
double * a,
double * b,
double * weight )

Algorithm for weighting the problem that is given to a LLSQ algorithm.

Square roots of weights are used because in LLSQ algorithms the difference w*A-w*b is squared.

See also
llsqWghtSquared
Returns
Algorithm returns zero if successful, 1 if arguments are inappropriate.
Parameters
NMatrix A dimension N (nr of parameters).
MMatrix A dimension M (nr of samples).
APointer to matrix A[N][M]; enter NULL to use following matrix format instead.
aPointer to matrix A[N*M]; enter NULL to use previous matrix format instead.
bVector B of length M.
weightWeights for each sample (array of length M).

Definition at line 419 of file bvls.c.

432 {
433 int n, m;
434 double *w;
435
436 /* Check the arguments */
437 if(N<1 || M<1 || (A==NULL && a==NULL) || b==NULL || weight==NULL) return(1);
438
439 /* Allocate memory */
440 w=(double*)malloc(M*sizeof(double)); if(w==NULL) return(2);
441
442 /* Check that weights are not zero and get the square roots of them to w[] */
443 for(m=0; m<M; m++) {
444 if(weight[m]<=1.0e-20) w[m]=0.0;
445 else w[m]=sqrt(weight[m]);
446 }
447
448 /* Multiply rows of matrix A and elements of vector b with weights*/
449 for(m=0; m<M; m++) {
450 for(n=0; n<N; n++) {
451 if(A!=NULL) A[n][m]*=w[m];
452 if(a!=NULL) a[m+n*M]*=w[m];
453 }
454 b[m]*=w[m];
455 }
456
457 free(w);
458 return(0);
459}

◆ llsqWghtSquared()

int llsqWghtSquared ( int N,
int M,
double ** A,
double * a,
double * b,
double * sweight )

Algorithm for weighting the problem that is given to a LLSQ algorithm.

Square roots of weights are used because in LLSQ algorithms 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.

See also
llsqWght
Returns
Algorithm returns zero if successful, 1 if arguments are inappropriate.
Parameters
NMatrix A dimension N (nr of parameters).
MMatrix A dimension M (nr of samples).
APointer to matrix A[N][M]; enter NULL to use following matrix format instead.
aPointer to matrix A[N*M]; enter NULL to use previous matrix format instead.
bVector B of length M.
sweightSquared weights for each sample (array of length M).

Definition at line 474 of file bvls.c.

487 {
488 int n, m;
489
490 /* Check the arguments */
491 if(N<1 || M<1 || (A==NULL && a==NULL) || b==NULL || sweight==NULL) return(1);
492
493 /* Multiply rows of matrix A and elements of vector b with weights*/
494 for(m=0; m<M; m++) {
495 for(n=0; n<N; n++) {
496 if(A!=NULL) A[n][m]*=sweight[m];
497 if(a!=NULL) a[m+n*M]*=sweight[m];
498 }
499 b[m]*=sweight[m];
500 }
501
502 return(0);
503}