NAPISD
PAHdb website C++ backend
Loading...
Searching...
No Matches
NNLS.cpp
1/*****************************************************************************
2
3 NNLS.cpp
4
5 http://nccastaff.bournemouth.ac.uk/jmacey/MastersProjects/MSc2010/09SteveTwist/html/files.html
6
7 Version 2009-04-16
8
9 Algorithm NNLS (Non-negative least-squares)
10
11 Implements the Lawson-Hanson NNLS algorithm
12
13 Given an m by n matrix A, and an m-vector B, computes an n-vector X, that
14 solves the least squares problem A * X = B , subject to X>=0
15
16 Instead of pointers for working space, NULL can be given to let this function
17 to allocate and free the required memory.
18
19 Returns:
20 Function returns 0 if succesful, 1, if iteration count exceeded 3*N, or 2 in
21 case of invalid problem dimensions or memory allocation error. Parameters: a
22 On entry, a[ 0... N ][ 0 ... M ] contains the M by N matrix A. On exit, a[][]
23 contains the product matrix Q*A, where Q is an m by n orthogonal matrix
24 generated implicitly by this function. m Matrix dimension m n Matrix
25 dimension n b On entry, b[] must contain the m-vector B. On exit, b[] contains
26 Q*B x On exit, x[] will contain the solution vector rnorm On exit, rnorm
27 contains the Euclidean norm of the residual vector. If NULL is given, no rnorm
28 is calculated
29 wp An n-array of working space, wp[]. On exit, wp[] will contain the dual
30 solution vector. wp[i]=0.0 for all i in set p and wp[i]<=0.0 for all i in set
31 z. Can be NULL, which causes this algorithm to allocate memory for it. zzp
32 An m-array of working space, zz[]. Can be NULL, which causes this algorithm to
33 allocate memory for it.
34 indexp An n-array of working space, index[]. Can be NULL, which causes
35 this algorithm to allocate memory for it.
36
37 *****************************************************************************/
38
39#include <NNLS.h>
40
41int nnls(double **a, int m, int n, double *b, double *x, double *rnorm,
42 double *wp, double *zzp, int *indexp) {
43
44 int pfeas, ret = 0, iz, jz, iz1, iz2, npp1, *index;
45 double d1, d2, sm, up = 0.0, ss, *w, *zz;
46 int iter, k, j = 0, l, itmax, izmax = 0, nsetp, ii, jj = 0, ip;
47 double temp, wmax, t, alpha, asave, dummy, unorm, ztest, cc;
48
49 /* Check the parameters and data */
50 if (m <= 0 || n <= 0 || a == NULL || b == NULL || x == NULL)
51 return (2);
52 /* Allocate memory for working space, if required */
53 if (wp != NULL)
54 w = wp;
55 else
56 w = (double *)calloc(n, sizeof(double));
57 if (zzp != NULL)
58 zz = zzp;
59 else
60 zz = (double *)calloc(m, sizeof(double));
61 if (indexp != NULL)
62 index = indexp;
63 else
64 index = (int *)calloc(n, sizeof(int));
65 if (w == NULL || zz == NULL || index == NULL)
66 return (2);
67
68 /* Initialize the arrays INDEX[] and X[] */
69 for (k = 0; k < n; k++) {
70 x[k] = 0.;
71 index[k] = k;
72 }
73 iz2 = n - 1;
74 iz1 = 0;
75 nsetp = 0;
76 npp1 = 0;
77
78 /* Main loop; quit if all coeffs are already in the solution or */
79 /* if M cols of A have been triangularized */
80 iter = 0;
81 if (n < 3)
82 itmax = n * 3;
83 else
84 itmax = n * n;
85 while (iz1 <= iz2 && nsetp < m) {
86 /* Compute components of the dual (negative gradient) vector W[] */
87 for (iz = iz1; iz <= iz2; iz++) {
88 j = index[iz];
89 sm = 0.;
90 for (l = npp1; l < m; l++)
91 sm += a[j][l] * b[l];
92 w[j] = sm;
93 }
94
95 while (1) {
96
97 /* Find largest positive W[j] */
98 for (wmax = 0., iz = iz1; iz <= iz2; iz++) {
99 j = index[iz];
100 if (w[j] > wmax) {
101 wmax = w[j];
102 izmax = iz;
103 }
104 }
105
106 /* Terminate if wmax<=0.; */
107 /* it indicates satisfaction of the Kuhn-Tucker conditions */
108 if (wmax <= 0.0)
109 break;
110 iz = izmax;
111 j = index[iz];
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 */
115 /* near linear dependence. */
116 asave = a[j][npp1];
117 h12(1, npp1, npp1 + 1, m, &a[j][0], 1, &up, &dummy, 1, 1, 0);
118 unorm = 0.;
119 if (nsetp != 0)
120 for (l = 0; l < nsetp; l++) {
121 d1 = a[j][l];
122 unorm += d1 * d1;
123 }
124 unorm = sqrt(unorm);
125 d2 = unorm + (d1 = a[j][npp1], fabs(d1)) * 0.01;
126 if ((d2 - unorm) > 0.) {
127 /* Col j is sufficiently independent. Copy B into ZZ, update ZZ */
128 /* and solve for ztest ( = proposed new value for X[j] ) */
129 for (l = 0; l < m; l++)
130 zz[l] = b[l];
131 h12(2, npp1, npp1 + 1, m, &a[j][0], 1, &up, zz, 1, 1, 1);
132 ztest = zz[npp1] / a[j][npp1];
133 /* See if ztest is positive */
134 if (ztest > 0.)
135 break;
136 }
137
138 /* Reject j as a candidate to be moved from set Z to set P. Restore */
139 /* A[npp1,j], set W[j]=0., and loop back to test dual coeffs again */
140 a[j][npp1] = asave;
141 w[j] = 0.;
142 } /* while(1) */
143 if (wmax <= 0.0)
144 break;
145
146 /* Index j=INDEX[iz] 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 subdiagonal elts in col j, set W[j]=0. */
149 for (l = 0; l < m; ++l)
150 b[l] = zz[l];
151 index[iz] = index[iz1];
152 index[iz1] = j;
153 iz1++;
154 nsetp = npp1 + 1;
155 npp1++;
156 if (iz1 <= iz2)
157 for (jz = iz1; jz <= iz2; jz++) {
158 jj = index[jz];
159 h12(2, nsetp - 1, npp1, m, &a[j][0], 1, &up, &a[jj][0], 1, m, 1);
160 }
161 if (nsetp != m)
162 for (l = npp1; l < m; l++)
163 a[j][l] = 0.;
164 w[j] = 0.;
165
166 /* Solve the triangular system; store the solution temporarily in Z[] */
167 for (l = 0; l < nsetp; l++) {
168 ip = nsetp - (l + 1);
169 if (l != 0)
170 for (ii = 0; ii <= ip; ii++)
171 zz[ii] -= a[jj][ii] * zz[ip + 1];
172 jj = index[ip];
173 zz[ip] /= a[jj][ip];
174 }
175
176 /* Secondary loop begins here */
177 while (++iter < itmax) {
178 /* See if all new constrained coeffs are feasible; if not, compute alpha
179 */
180 for (alpha = 2.0, ip = 0; ip < nsetp; ip++) {
181 l = index[ip];
182 if (zz[ip] <= 0.) {
183 t = -x[l] / (zz[ip] - x[l]);
184 if (alpha > t) {
185 alpha = t;
186 jj = ip - 1;
187 }
188 }
189 }
190
191 /* If all new constrained coeffs are feasible then still alpha==2. */
192 /* If so, then exit from the secondary loop to main loop */
193 if (alpha == 2.0)
194 break;
195
196 /* Use alpha (0.<alpha<1.) to interpolate between old X and new ZZ */
197 for (ip = 0; ip < nsetp; ip++) {
198 l = index[ip];
199 x[l] += alpha * (zz[ip] - x[l]);
200 }
201
202 /* Modify A and B and the INDEX arrays to move coefficient i */
203 /* from set P to set Z. */
204 k = index[jj + 1];
205 pfeas = 1;
206 do {
207 x[k] = 0.;
208 if (jj != (nsetp - 1)) {
209 jj++;
210 for (j = jj + 1; j < nsetp; j++) {
211 ii = index[j];
212 index[j - 1] = ii;
213 g1(a[ii][j - 1], a[ii][j], &cc, &ss, &a[ii][j - 1]);
214 for (a[ii][j] = 0., l = 0; l < n; l++)
215 if (l != ii) {
216 /* Apply procedure G2 (CC,SS,A(J-1,L),A(J,L)) */
217 temp = a[l][j - 1];
218 a[l][j - 1] = cc * temp + ss * a[l][j];
219 a[l][j] = -ss * temp + cc * a[l][j];
220 }
221 /* Apply procedure G2 (CC,SS,B(J-1),B(J)) */
222 temp = b[j - 1];
223 b[j - 1] = cc * temp + ss * b[j];
224 b[j] = -ss * temp + cc * b[j];
225 }
226 }
227 npp1 = nsetp - 1;
228 nsetp--;
229 iz1--;
230 index[iz1] = k;
231
232 /* See if the remaining coeffs in set P are feasible; they should */
233 /* be because of the way alpha was determined. If any are */
234 /* infeasible it is due to round-off error. Any that are */
235 /* nonpositive will be set to zero and moved from set P to set Z */
236 for (jj = 0, pfeas = 1; jj < nsetp; jj++) {
237 k = index[jj];
238 if (x[k] <= 0.) {
239 pfeas = 0;
240 break;
241 }
242 }
243 } while (pfeas == 0);
244
245 /* Copy B[] into zz[], then solve again and loop back */
246 for (k = 0; k < m; k++)
247 zz[k] = b[k];
248 for (l = 0; l < nsetp; l++) {
249 ip = nsetp - (l + 1);
250 if (l != 0)
251 for (ii = 0; ii <= ip; ii++)
252 zz[ii] -= a[jj][ii] * zz[ip + 1];
253 jj = index[ip];
254 zz[ip] /= a[jj][ip];
255 }
256 } /* end of secondary loop */
257
258 if (iter >= itmax) {
259 ret = 1;
260 break;
261 }
262 for (ip = 0; ip < nsetp; ip++) {
263 k = index[ip];
264 x[k] = zz[ip];
265 }
266 } /* end of main loop */
267 /* Compute the norm of the final residual vector */
268 sm = 0.;
269
270 if (rnorm != NULL) {
271 if (npp1 < m)
272 for (k = npp1; k < m; k++)
273 sm += (b[k] * b[k]);
274 else
275 for (j = 0; j < n; j++)
276 w[j] = 0.;
277 *rnorm = sqrt(sm);
278 }
279
280 /* Free working space, if it was allocated here */
281 if (wp == NULL)
282 free(w);
283 if (zzp == NULL)
284 free(zz);
285 if (indexp == NULL)
286 free(index);
287 return (ret);
288} /* nnls_ */
289
290int h12(int mode, int lpivot, int l1, int m, double *u, int u_dim1, double *up,
291 double *cm, int ice, int icv, int ncv) {
292 double d1, b, clinv, cl, sm;
293 int k, j;
294
295 /* Check parameters */
296 if (mode != 1 && mode != 2)
297 return (1);
298 if (m < 1 || u == NULL || u_dim1 < 1 || cm == NULL)
299 assert(0);
300
301 if (lpivot < 0 || lpivot >= l1 || l1 > m)
302 return (1);
303
304 /* Function Body */
305 cl = NNLS_ABS(u[lpivot * u_dim1]);
306
307 if (mode == 2) { /* Apply transformation I+U*(U**T)/B to cm[] */
308 if (cl <= 0.)
309 return (0);
310 } else { /* Construct the transformation */
311
312 /* Trying to compensate overflow */
313 for (j = l1; j < m; j++) { // Computing MAX
314 cl = NNLS_MAX(NNLS_ABS(u[j * u_dim1]), cl);
315 }
316 // zero vector?
317
318 if (cl <= 0.)
319 return (0);
320
321 clinv = 1.0 / cl;
322
323 // Computing 2nd power
324 d1 = u[lpivot * u_dim1] * clinv;
325 sm = d1 * d1;
326
327 for (j = l1; j < m; j++) {
328 d1 = u[j * u_dim1] * clinv;
329 sm += d1 * d1;
330 }
331 cl *= sqrt(sm);
332
333 if (u[lpivot * u_dim1] > 0.)
334 cl = -cl;
335 up[0] = u[lpivot * u_dim1] - cl;
336 u[lpivot * u_dim1] = cl;
337 }
338
339 // no vectors where to apply? only change pivot vector!
340
341 b = up[0] * u[lpivot * u_dim1];
342
343 /* b must be nonpositive here; if b>=0., then return */
344 if (b == 0)
345 return (0);
346
347 // ok, for all vectors we want to apply
348 for (j = 0; j < ncv; j++) {
349 // take s = c[p,j]*h + sigma(i=l..m){ c[i,j] *v [ i ] }
350 sm = cm[lpivot * ice + j * icv] * (up[0]);
351 for (k = l1; k < m; k++) {
352 sm += cm[k * ice + j * icv] * u[k * u_dim1];
353 }
354
355 if (sm != 0.0) {
356 sm *= (1 / b);
357
358 cm[lpivot * ice + j * icv] += sm * (up[0]);
359 for (k = l1; k < m; k++) {
360 cm[k * ice + j * icv] += u[k * u_dim1] * sm;
361 }
362 }
363 }
364
365 return (0);
366} /* lss_h12 */
367
368void g1(double a, double b, double *cterm, double *sterm, double *sig) {
369 double d1, xr, yr;
370
371 if (fabs(a) > fabs(b)) {
372 xr = b / a;
373 d1 = xr;
374 yr = sqrt(d1 * d1 + 1.);
375 d1 = 1. / yr;
376 *cterm = (a >= 0.0 ? fabs(d1) : -fabs(d1));
377 *sterm = (*cterm) * xr;
378 *sig = fabs(a) * yr;
379 } else if (b != 0.) {
380 xr = a / b;
381 d1 = xr;
382 yr = sqrt(d1 * d1 + 1.);
383 d1 = 1. / yr;
384 *sterm = (b >= 0.0 ? fabs(d1) : -fabs(d1));
385 *cterm = (*sterm) * xr;
386 *sig = fabs(b) * yr;
387 } else {
388 *sig = 0.;
389 *cterm = 0.;
390 *sterm = 1.;
391 }
392} /* g1 */

Since FY2019 the NASA Ames PAH IR Spectroscopic Database is being supported through a directed Work Package at NASA Ames titled: "Laboratory Astrophysics - The NASA Ames PAH IR Spectroscopic Database".
Since FY2023 the NASA Ames PAH IR Spectroscopic Database is being supported through the Laboratory Astrophysics Rd 2 directed Work Package at NASA Ames.
© Copyright 2021-2025, Christiaan Boersma