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

BFM for the sum of decaying exponential functions. More...

#include "tpcclibConfig.h"
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
#include <string.h>
#include "tpcextensions.h"
#include "tpclinopt.h"
#include "tpcbfm.h"

Go to the source code of this file.

Functions

int spectralDExp (const double *x, const double *y, double *w, const int snr, const double kmin, const double kmax, const int bnr, double *k, double *a, double *yfit, TPCSTATUS *status)
int spectralKRange (double *k, double *a, const int n, double *kmin, double *kmax, TPCSTATUS *status)
int spectralBFNr (double *k, double *a, const int n)
int spectralBFExtract (double *k, double *a, const int n, double *ke, double *ae, const int ne)

Detailed Description

BFM for the sum of decaying exponential functions.

Definition in file bf_dexp.c.

Function Documentation

◆ spectralBFExtract()

int spectralBFExtract ( double * k,
double * a,
const int n,
double * ke,
double * ae,
const int ne )

After spectral x,y-data fitting to the sum of decaying exponential functions, extract the zero-separated (positive a) basis functions we actually got.

The maximum number of basis function parameters to be extracted must be specified; if more are found, then the ones with smallest a are averaged. If less are found, then the rest of output arrays are filled with zeroes, and the returned actual number is smaller than the requested number.

Returns
The number of extracted basis functions, or 0 in case of an error.
Author
Vesa Oikonen
See also
spectralDExp, spectralBFNr, spectralKRange
Parameters
kPointer to k array of length n; must be sorted to either order, as those usually are. Contents are not modified.
aPointer to a array of length n; not modified.
nLength of k and a arrays.
kePointer to an allocated array of extracted k values of length ne.
aePointer to an allocated array of extracted a values of length ne; not modified.
neLength of k and a arrays, and the maximum number of parameters to extract.

Definition at line 227 of file bf_dexp.c.

241 {
242 if(k==NULL || a==NULL || n<1 || ke==NULL || ae==NULL || ne<1) return(0);
243
244 /* First, extract all zero-separated basis function clusters */
245 double *buf=NULL, *ok, *oa;
246 int bfn=spectralBFNr(k, a, n); if(bfn==0) return(0);
247 if(bfn>ne) {
248 /* we need to allocate memory locally */
249 buf=(double*)malloc(2*bfn*sizeof(double)); if(buf==NULL) return(0);
250 ok=buf; oa=buf+bfn;
251 } else {
252 /* we just set pointers to user-provided arrays */
253 ok=ke; oa=ae;
254 }
255 {
256 int nn=0, i=0, ii=0;
257 do {
258 i=doubleCSpanPositives(a+ii, n-ii);
259 ii+=i; if(n-ii<1) break;
260 i=doubleSpanPositives(a+ii, n-ii);
261 if(i==1) { // cluster contains just one positive a
262 oa[nn]=a[ii]; ok[nn]=k[ii];
263 nn++;
264 } else if(i>1) { // cluster contains more than one positive a
265 /* get sum of a values in the cluster */
266 oa[nn]=doubleSum(a+ii, i);
267 /* get weighted mean of k values in the cluster */
268 ok[nn]=doubleWMean(k+ii, a+ii, i);
269 nn++;
270 }
271 ii+=i;
272 } while(ii<n && nn<bfn);
273 if(0) {
274 printf("\n\tCluster a and k values\n");
275 for(int i=0; i<bfn; i++) printf("\t%g\t%g\n", oa[i], ok[i]);
276 printf("\n"); fflush(stdout);
277 }
278 }
279 /* If the number needs not to be reduced, then we are ready */
280 if(ne>=bfn) {
281 for(int i=bfn; i<ne; i++) ke[i]=ae[i]=0.0;
282 if(buf!=NULL) free(buf);
283 return(bfn);
284 }
285
286 while(ne<bfn) {
287 /* Search the smallest a parameter */
288 int ami=doubleAbsMinIndex(oa, bfn);
289 if(0) printf("smallest |a[%d]|=%g\n", ami, oa[ami]);
290 /* Combine it with previous or next cluster? */
291 int ci;
292 if(ami==0) ci=1;
293 else if(ami==bfn-1) ci=bfn-2;
294 else {if(fabs(oa[ami-1])<fabs(oa[ami+1])) ci=ami-1; else ci=ami+1;}
295 if(0) printf("combining clusters %d and %d\n", 1+ami, 1+ci);
296 /* Replace the cluster with combination of two clusters */
297 ok[ci]=(oa[ami]*ok[ami]+oa[ci]*ok[ci])/(oa[ami]+oa[ci]);
298 oa[ci]+=oa[ami];
299 /* Delete the cluster */
300 for(int i=ami+1; i<bfn; i++) oa[i-1]=oa[i];
301 for(int i=ami+1; i<bfn; i++) ok[i-1]=ok[i];
302 bfn--;
303 if(0) {
304 printf("\n\tAfter reduction, cluster a and k values\n");
305 for(int i=0; i<bfn; i++) printf("\t%g\t%g\n", oa[i], ok[i]);
306 printf("\n"); fflush(stdout);
307 }
308 }
309 for(int i=0; i<ne; i++) ae[i]=oa[i];
310 for(int i=0; i<ne; i++) ke[i]=ok[i];
311 if(buf!=NULL) free(buf);
312
313 return(bfn);
314}
int spectralBFNr(double *k, double *a, const int n)
Definition bf_dexp.c:193
int doubleSpanPositives(double *a, const int n)
Definition doubleutil.c:302
unsigned int doubleAbsMinIndex(double *a, const unsigned int n)
Definition doubleutil.c:416
int doubleCSpanPositives(double *a, const int n)
Definition doubleutil.c:320
double doubleWMean(double *a, double *w, const unsigned int n)
Definition doubleutil.c:244
double doubleSum(double *a, const unsigned int n)
Definition doubleutil.c:206

◆ spectralBFNr()

int spectralBFNr ( double * k,
double * a,
const int n )

After spectral x,y-data fitting to the sum of decaying exponential functions, determine the number of zero-separated (positive a) basis functions we actually got.

Returns
The number of basis functions, or 0 in case of an error.
Author
Vesa Oikonen
See also
spectralDExp, spectralBFExtract, spectralKRange
Parameters
kPointer to k array of length n; does not need to be in specific order, although those usually are. Contents are not modified.
aPointer to a array of length n; not modified.
nLength of k and a arrays.

Definition at line 193 of file bf_dexp.c.

201 {
202 if(k==NULL || a==NULL || n<1) return(0);
203 int bfn=0, i=0, ii=0;
204 do {
205 i=doubleCSpanPositives(a+ii, n-ii);
206 ii+=i; if(n-ii<1) break;
207 i=doubleSpanPositives(a+ii, n-ii);
208 ii+=i; if(i>0) bfn++;
209 } while(ii<n);
210 return(bfn);
211}

Referenced by spectralBFExtract().

◆ spectralDExp()

int spectralDExp ( const double * x,
const double * y,
double * w,
const int snr,
const double kmin,
const double kmax,
const int bnr,
double * k,
double * a,
double * yfit,
TPCSTATUS * status )

Spectral x,y-data fitting to the sum of decaying exponential functions.

f(x) = a1*exp(-k1*x) + a2*exp(-k2*x) + a3*exp(-k3*x) + ... where aN>=0 and kN>0.

Todo
Add tests.
Returns
enum tpcerror (TPCERROR_OK when successful).
Author
Vesa Oikonen
See also
spectralKRange, spectralBFNr, nnls, nnlsWght, tacExtractRange
Parameters
xPointer to TAC x data (not modified). Data must be sorted by increasing x. Negative or missing x values are not allowed.
yPointer to TAC y data (not modified). Data must be sorted by increasing x. Missing y values are not allowed.
wPointer to TAC sample weights (not modified). Enter NULL if not needed.
snrNumber of samples in x[] and y[]; at least 3.
kminMinimum of k (set based on sample range and unit); must be >0.
kmaxMaximum of k (set based on sample range and unit); must be >kmin.
bnrNumber of basis functions to calculate; also the length of arrays k[] and a[]; at least 4.
kPointer to array of length bnr for k values, filled in by this function.
aPointer to array of length bnr for a values, filled in by this function; notice that most values may be set to zero.
yfitPointer to array for fitted y values. Enter NULL if not needed.
statusPointer to status data; enter NULL if not needed

Definition at line 28 of file bf_dexp.c.

55 {
56 int verbose=0; if(status!=NULL) verbose=status->verbose;
57 if(verbose>0) printf("%s()\n", __func__);
58 if(x==NULL || y==NULL || k==NULL || a==NULL || snr<1 || bnr<1) {
59 statusSet(status, __func__, __FILE__, __LINE__, TPCERROR_NO_DATA);
60 return TPCERROR_NO_DATA;
61 }
62 if(snr<3 || bnr<4) {
63 if(verbose>1) fprintf(stderr, "invalid sample or BF number\n");
64 statusSet(status, __func__, __FILE__, __LINE__, TPCERROR_TOO_FEW);
65 return TPCERROR_TOO_FEW;
66 }
67 if(!(kmin>0.0) || kmin>=kmax) { // range cannot be computed otherwise
68 if(verbose>1) fprintf(stderr, "invalid k range\n");
69 statusSet(status, __func__, __FILE__, __LINE__, TPCERROR_INVALID_VALUE);
71 }
72 int ret;
73
74 if(verbose>1) printf("computing k values\n");
75 {
76 double a, b, c;
77 a=log10(kmin); b=log10(kmax); c=(b-a)/(double)(bnr-1);
78 if(verbose>3) printf(" a := %g\n b := %g\n c := %g\n", a, b, c);
79 for(int bi=0; bi<bnr; bi++) k[bi]=pow(10.0, (double)bi*c+a);
80 }
81
82 /* Allocate memory required by NNLS */
83 int nnls_n, nnls_m, n, m, nnls_index[bnr];
84 double *nnls_a[bnr], *nnls_b, *nnls_zz, nnls_x[bnr], *nnls_mat,
85 nnls_wp[bnr], *dptr, nnls_rnorm;
86 if(verbose>1) printf("allocating memory for NNLS\n");
87 nnls_n=bnr; nnls_m=snr;
88 nnls_mat=(double*)malloc(((nnls_n+2)*nnls_m)*sizeof(double));
89 if(nnls_mat==NULL) {
90 if(verbose>1) fprintf(stderr, "Error: cannot allocate memory for NNLS.\n");
91 statusSet(status, __func__, __FILE__, __LINE__, TPCERROR_OUT_OF_MEMORY);
93 }
94 for(n=0, dptr=nnls_mat; n<nnls_n; n++) {nnls_a[n]=dptr; dptr+=nnls_m;}
95 nnls_b=dptr; dptr+=nnls_m; nnls_zz=dptr;
96
97 if(verbose>1) printf("filling NNLS data matrix\n");
98 /* Fill NNLS B array with measured y values */
99 for(m=0; m<nnls_m; m++) nnls_b[m]=y[m];
100
101 /* Fill NNLS A matrix with basis functions, calculated in place */
102 for(n=0; n<nnls_n; n++)
103 for(m=0; m<nnls_m; m++)
104 nnls_a[n][m]=exp(-k[n]*x[m]);
105
106 /* Apply weights, if given */
107 if(w!=NULL) nnlsWght(nnls_n, nnls_m, nnls_a, nnls_b, w);
108
109 /* NNLS */
110 if(verbose>1) printf("applying NNLS\n");
111 ret=nnls(nnls_a, nnls_m, nnls_n, nnls_b, nnls_x, &nnls_rnorm, nnls_wp, nnls_zz, nnls_index);
112 if(ret>1) {
113 if(verbose>1) fprintf(stderr, "NNLS solution not possible.\n");
114 free(nnls_mat);
115 statusSet(status, __func__, __FILE__, __LINE__, TPCERROR_NO_SOLUTION);
117 }
118 /* Copy a[] values */
119 if(a!=NULL) for(n=0; n<nnls_n; n++) a[n]=nnls_x[n];
120
121 /* Compute fitted y[] */
122 if(yfit!=NULL) {
123 if(verbose>1) printf("computing yfit[]\n");
124 for(m=0; m<nnls_m; m++) {
125 yfit[m]=0.0;
126 for(n=0; n<nnls_n; n++)
127 if(nnls_x[n]>0.0)
128 yfit[m]+=nnls_x[n]*exp(-k[n]*x[m]);
129 }
130 }
131 free(nnls_mat);
132
133 statusSet(status, __func__, __FILE__, __LINE__, TPCERROR_OK);
134 return TPCERROR_OK;
135}
int nnls(double **a, int m, int n, double *b, double *x, double *rnorm, double *wp, double *zzp, int *indexp)
Definition nnls.c:48
int nnlsWght(int N, int M, double **A, double *b, double *weight)
Definition nnls.c:264
void statusSet(TPCSTATUS *s, const char *func, const char *srcfile, int srcline, tpcerror error)
Definition statusmsg.c:142
int verbose
Verbose level, used by statusPrint() etc.
@ TPCERROR_INVALID_VALUE
Invalid value.
@ TPCERROR_NO_SOLUTION
No solution.
@ TPCERROR_OUT_OF_MEMORY
Cannot allocate memory.
@ TPCERROR_OK
No error.
@ TPCERROR_NO_DATA
File contains no data.
@ TPCERROR_TOO_FEW
File contains too few samples.

◆ spectralKRange()

int spectralKRange ( double * k,
double * a,
const int n,
double * kmin,
double * kmax,
TPCSTATUS * status )

After spectral x,y-data fitting to the sum of decaying exponential functions, determine the min and max of k values with positive a value.

Returns
enum tpcerror (TPCERROR_OK when successful).
Author
Vesa Oikonen
See also
spectralDExp, spectralBFNr
Parameters
kPointer to k array of length n; does not need to be in specific order, although those usually are. Contents are not modified.
aPointer to a array of length n; not modified.
nLength of k and a arrays.
kminPointer min k value. Enter NULL if not needed.
kmaxPointer max k value. Enter NULL if not needed.
statusPointer to status data; enter NULL if not needed

Definition at line 145 of file bf_dexp.c.

159 {
160 int verbose=0; if(status!=NULL) verbose=status->verbose;
161 if(verbose>0) printf("%s(k[], a[], %d, *kmin, *kmax)\n", __func__, n);
162 if(k==NULL || a==NULL || n<1) {
163 statusSet(status, __func__, __FILE__, __LINE__, TPCERROR_NO_DATA);
164 return TPCERROR_NO_DATA;
165 }
166 double max=nan(""), min=nan("");
167 for(int i=0; i<n; i++) if(a[i]>0.0 && isfinite(k[i])) {
168 if(isnan(max) || k[i]>max) max=k[i];
169 if(isnan(min) || k[i]<min) min=k[i];
170 }
171 if(verbose>2) {
172 printf(" kmin := %g\n", min);
173 printf(" kmax := %g\n", max);
174 }
175 if(kmin!=NULL) *kmin=min;
176 if(kmax!=NULL) *kmax=max;
177 if(isnan(min) || isnan(max)) {
178 statusSet(status, __func__, __FILE__, __LINE__, TPCERROR_NO_VALUE);
179 return TPCERROR_NO_VALUE;
180 }
181 statusSet(status, __func__, __FILE__, __LINE__, TPCERROR_OK);
182 return TPCERROR_OK;
183}
@ TPCERROR_NO_VALUE
Value not found.