Annotation of OpenXM_contrib/gnuplot/fit.c, Revision 1.1.1.2
1.1 maekawa 1: #ifndef lint
1.1.1.2 ! maekawa 2: static char *RCSid = "$Id: fit.c,v 1.23.2.3 1999/10/01 18:22:14 lhecking Exp $";
1.1 maekawa 3: #endif
4:
5: /*
6: * Nonlinear least squares fit according to the
7: * Marquardt-Levenberg-algorithm
8: *
9: * added as Patch to Gnuplot (v3.2 and higher)
10: * by Carsten Grammes
11: * Experimental Physics, University of Saarbruecken, Germany
12: *
13: * Internet address: cagr@rz.uni-sb.de
14: *
15: * Copyright of this module: 1993, 1998 Carsten Grammes
16: *
17: * Permission to use, copy, and distribute this software and its
18: * documentation for any purpose with or without fee is hereby granted,
19: * provided that the above copyright notice appear in all copies and
20: * that both that copyright notice and this permission notice appear
21: * in supporting documentation.
22: *
23: * This software is provided "as is" without express or implied warranty.
24: *
25: * 930726: Recoding of the Unix-like raw console I/O routines by:
26: * Michele Marziani (marziani@ferrara.infn.it)
27: * drd: start unitialised variables at 1 rather than NEARLY_ZERO
28: * (fit is more likely to converge if started from 1 than 1e-30 ?)
29: *
30: * HBB (Broeker@physik.rwth-aachen.de) : fit didn't calculate the errors
31: * in the 'physically correct' (:-) way, if a third data column containing
32: * the errors (or 'uncertainties') of the input data was given. I think
33: * I've fixed that, but I'm not sure I really understood the M-L-algo well
34: * enough to get it right. I deduced my change from the final steps of the
35: * equivalent algorithm for the linear case, which is much easier to
36: * understand. (I also made some minor, mostly cosmetic changes)
37: *
38: * HBB (again): added error checking for negative covar[i][i] values and
39: * for too many parameters being specified.
40: *
41: * drd: allow 3d fitting. Data value is now called fit_z internally,
42: * ie a 2d fit is z vs x, and a 3d fit is z vs x and y.
43: *
44: * Lars Hecking : review update command, for VMS in particular, where
45: * it is not necessary to rename the old file.
46: *
47: * HBB, 971023: lifted fixed limit on number of datapoints, and number
48: * of parameters.
49: */
50:
51:
52: #define FIT_MAIN
53:
54: #include <signal.h>
55:
56: #include "plot.h"
57: #include "matrix.h"
58: #include "fit.h"
59: #include "setshow.h" /* for load_range */
60: #include "alloc.h"
61:
62: #if defined(MSDOS) || defined(DOS386) /* non-blocking IO stuff */
63: # include <io.h>
64: # ifndef _Windows /* WIN16 does define MSDOS .... */
65: # include <conio.h>
66: # endif
67: # include <dos.h>
68: #else /* !(MSDOS || DOS386) */
69: # ifndef VMS
70: # include <fcntl.h>
71: # endif /* !VMS */
72: #endif /* !(MSDOS || DOS386) */
73:
74: #if defined(ATARI) || defined(MTOS)
75: # define getchx() Crawcin()
76: static int kbhit(void);
77: #endif
78:
79: #define STANDARD stderr /* compatible with gnuplot philosophy */
80:
81: #define BACKUP_SUFFIX ".old"
82:
83:
84: /* access external global variables (ought to make a globals.h someday) */
85:
86: extern struct udft_entry *dummy_func;
87: extern char dummy_var[MAX_NUM_VAR][MAX_ID_LEN+1];
88: extern char c_dummy_var[MAX_NUM_VAR][MAX_ID_LEN+1];
89: extern int c_token;
90: extern int df_datum, df_line_number;
91:
1.1.1.2 ! maekawa 92: /* following 2 external arrays are needed to use time data */
! 93: extern int datatype[];
! 94: extern int df_timecol[];
1.1 maekawa 95:
96: enum marq_res {
97: OK, ERROR, BETTER, WORSE
98: };
99: typedef enum marq_res marq_res_t;
100:
101: #ifdef INFINITY
102: # undef INFINITY
103: #endif
104:
105: #define INFINITY 1e30
106: #define NEARLY_ZERO 1e-30
107:
108: /* create new variables with this value (was NEARLY_ZERO) */
109: #define INITIAL_VALUE 1.0
110:
111: /* Relative change for derivatives */
112: #define DELTA 0.001
113:
114: #define MAX_DATA 2048
115: #define MAX_PARAMS 32
116: #define MAX_LAMBDA 1e20
117: #define MIN_LAMBDA 1e-20
118: #define LAMBDA_UP_FACTOR 10
119: #define LAMBDA_DOWN_FACTOR 10
120:
121: #if defined(MSDOS) || defined(OS2) || defined(DOS386)
122: # define PLUSMINUS "\xF1" /* plusminus sign */
123: #else
124: # define PLUSMINUS "+/-"
125: #endif
126:
127: #define LASTFITCMDLENGTH 511
128:
129: /* HBB 971023: new, allow for dynamic adjustment of these: */
130: static int max_data;
131: static int max_params;
132:
133: static double epsilon = 1e-5; /* convergence limit */
134: static int maxiter = 0; /* HBB 970304: maxiter patch */
135:
136: static char fit_script[128];
137: static char logfile[128] = "fit.log";
138: static char *FIXED = "# FIXED";
139: static char *GNUFITLOG = "FIT_LOG";
140: static char *FITLIMIT = "FIT_LIMIT";
141: static char *FITSTARTLAMBDA = "FIT_START_LAMBDA";
142: static char *FITLAMBDAFACTOR = "FIT_LAMBDA_FACTOR";
143: static char *FITMAXITER = "FIT_MAXITER"; /* HBB 970304: maxiter patch */
144: static char *FITSCRIPT = "FIT_SCRIPT";
145: static char *DEFAULT_CMD = "replot"; /* if no fitscript spec. */
146: static char last_fit_command[LASTFITCMDLENGTH+1] = "";
147:
148:
149: static FILE *log_f = NULL;
150:
151: static int num_data, num_params;
152: static int columns;
153: static double *fit_x = 0, *fit_y = 0, *fit_z = 0, *err_data = 0, *a = 0;
154: static TBOOLEAN ctrlc_flag = FALSE;
155: static TBOOLEAN user_stop = FALSE;
156:
157: static struct udft_entry func;
158:
159: typedef char fixstr[MAX_ID_LEN+1];
160:
161: static fixstr *par_name;
162:
163: static double startup_lambda = 0, lambda_down_factor = LAMBDA_DOWN_FACTOR,
164: lambda_up_factor = LAMBDA_UP_FACTOR;
165:
166: /*****************************************************************
167: internal Prototypes
168: *****************************************************************/
169:
170: static RETSIGTYPE ctrlc_handle __PROTO((int an_int));
171: static void ctrlc_setup __PROTO((void));
172: static void printmatrix __PROTO((double **C, int m, int n));
173: static void print_matrix_and_vectors __PROTO((double **C, double *d, double *r, int m, int n));
174: static marq_res_t marquardt __PROTO((double a[], double **alpha, double *chisq,
175: double *lambda));
176: static TBOOLEAN analyze __PROTO((double a[], double **alpha, double beta[],
177: double *chisq));
178: static void calculate __PROTO((double *zfunc, double **dzda, double a[]));
179: static void call_gnuplot __PROTO((double *par, double *data));
180: static TBOOLEAN fit_interrupt __PROTO((void));
181: static TBOOLEAN regress __PROTO((double a[]));
182: static void show_fit __PROTO((int i, double chisq, double last_chisq, double *a,
183: double lambda, FILE * device));
184: static TBOOLEAN is_empty __PROTO((char *s));
185: static TBOOLEAN is_variable __PROTO((char *s));
186: static double getdvar __PROTO((char *varname));
187: static double createdvar __PROTO((char *varname, double value));
188: static void splitpath __PROTO((char *s, char *p, char *f));
189: static void backup_file __PROTO((char *, const char *));
190:
191:
192: /*****************************************************************
193: Small function to write the last fit command into a file
194: Arg: Pointer to the file; if NULL, nothing is written,
195: but only the size of the string is returned.
196: *****************************************************************/
197:
198: size_t wri_to_fil_last_fit_cmd(fp)
199: FILE *fp;
200: {
201: if (fp == NULL)
202: return strlen(last_fit_command);
203: else
204: return (size_t) fputs(last_fit_command, fp);
205: }
206:
207:
208: /*****************************************************************
209: This is called when a SIGINT occurs during fit
210: *****************************************************************/
211:
212: static RETSIGTYPE ctrlc_handle(an_int)
213: int an_int;
214: {
215: #ifdef OS2
216: (void) signal(an_int, SIG_ACK);
217: #else
218: /* reinstall signal handler (necessary on SysV) */
219: (void) signal(SIGINT, (sigfunc) ctrlc_handle);
220: #endif
221: ctrlc_flag = TRUE;
222: }
223:
224:
225: /*****************************************************************
226: setup the ctrl_c signal handler
227: *****************************************************************/
228: static void ctrlc_setup()
229: {
230: /*
231: * MSDOS defines signal(SIGINT) but doesn't handle it through
232: * real interrupts. So there remain cases in which a ctrl-c may
233: * be uncaught by signal. We must use kbhit() instead that really
234: * serves the keyboard interrupt (or write an own interrupt func
235: * which also generates #ifdefs)
236: *
237: * I hope that other OSes do it better, if not... add #ifdefs :-(
238: */
239: #if (defined(__EMX__) || !defined(MSDOS) && !defined(DOS386)) && !defined(ATARI) && !defined(MTOS)
240: (void) signal(SIGINT, (sigfunc) ctrlc_handle);
241: #endif
242: }
243:
244:
245: /*****************************************************************
246: getch that handles also function keys etc.
247: *****************************************************************/
248: #if defined(MSDOS) || defined(DOS386)
249:
250: /* HBB 980317: added a prototype... */
251: int getchx __PROTO((void));
252:
253: int getchx()
254: {
255: register int c = getch();
256: if (!c || c == 0xE0) {
257: c <<= 8;
258: c |= getch();
259: }
260: return c;
261: }
262: #endif
263:
264: /*****************************************************************
265: in case of fatal errors
266: *****************************************************************/
267: void error_ex()
268: {
269: char *sp;
270:
271: strncpy(fitbuf, " ", 9); /* start after GNUPLOT> */
272: sp = strchr(fitbuf, NUL);
273: while (*--sp == '\n')
274: ;
275: strcpy(sp + 1, "\n\n"); /* terminate with exactly 2 newlines */
276: fputs(fitbuf, STANDARD);
277: if (log_f) {
278: fprintf(log_f, "BREAK: %s", fitbuf);
279: (void) fclose(log_f);
280: log_f = NULL;
281: }
282: if (func.at) {
283: free(func.at); /* release perm. action table */
284: func.at = (struct at_type *) NULL;
285: }
286: /* restore original SIGINT function */
287: #if !defined(ATARI) && !defined(MTOS)
288: interrupt_setup();
289: #endif
290:
291: bail_to_command_line();
292: }
293:
294:
295: /*****************************************************************
296: New utility routine: print a matrix (for debugging the alg.)
297: *****************************************************************/
298: static void printmatrix(C, m, n)
299: double **C;
300: int m, n;
301: {
302: int i, j;
303:
304: for (i = 0; i < m; i++) {
305: for (j = 0; j < n - 1; j++)
306: Dblf2("%.8g |", C[i][j]);
307: Dblf2("%.8g\n", C[i][j]);
308: }
309: Dblf("\n");
310: }
311:
312: /**************************************************************************
313: Yet another debugging aid: print matrix, with diff. and residue vector
314: **************************************************************************/
315: static void print_matrix_and_vectors(C, d, r, m, n)
316: double **C;
317: double *d, *r;
318: int m, n;
319: {
320: int i, j;
321:
322: for (i = 0; i < m; i++) {
323: for (j = 0; j < n; j++)
324: Dblf2("%8g ", C[i][j]);
325: Dblf3("| %8g | %8g\n", d[i], r[i]);
326: }
327: Dblf("\n");
328: }
329:
330:
331: /*****************************************************************
332: Marquardt's nonlinear least squares fit
333: *****************************************************************/
334: static marq_res_t marquardt(a, C, chisq, lambda)
335: double a[];
336: double **C;
337: double *chisq;
338: double *lambda;
339: {
340: int i, j;
341: static double *da = 0, /* delta-step of the parameter */
342: *temp_a = 0, /* temptative new params set */
343: *d = 0, *tmp_d = 0, **tmp_C = 0, *residues = 0;
344: double tmp_chisq;
345:
346: /* Initialization when lambda == -1 */
347:
348: if (*lambda == -1) { /* Get first chi-square check */
349: TBOOLEAN analyze_ret;
350:
351: temp_a = vec(num_params);
352: d = vec(num_data + num_params);
353: tmp_d = vec(num_data + num_params);
354: da = vec(num_params);
355: residues = vec(num_data + num_params);
356: tmp_C = matr(num_data + num_params, num_params);
357:
358: analyze_ret = analyze(a, C, d, chisq);
359:
360: /* Calculate a useful startup value for lambda, as given by Schwarz */
361: /* FIXME: this is doesn't turn out to be much better, really... */
362: if (startup_lambda != 0)
363: *lambda = startup_lambda;
364: else {
365: *lambda = 0;
366: for (i = 0; i < num_data; i++)
367: for (j = 0; j < num_params; j++)
368: *lambda += C[i][j] * C[i][j];
369: *lambda = sqrt(*lambda / num_data / num_params);
370: }
371:
372: /* Fill in the lower square part of C (the diagonal is filled in on
373: each iteration, see below) */
374: for (i = 0; i < num_params; i++)
375: for (j = 0; j < i; j++)
376: C[num_data + i][j] = 0, C[num_data + j][i] = 0;
377: /*printmatrix(C, num_data+num_params, num_params); */
378: return analyze_ret ? OK : ERROR;
379: }
380: /* once converged, free dynamic allocated vars */
381:
382: if (*lambda == -2) {
383: free(d);
384: free(tmp_d);
385: free(da);
386: free(temp_a);
387: free(residues);
388: free_matr(tmp_C);
389: return OK;
390: }
391: /* Givens calculates in-place, so make working copies of C and d */
392:
393: for (j = 0; j < num_data + num_params; j++)
394: memcpy(tmp_C[j], C[j], num_params * sizeof(double));
395: memcpy(tmp_d, d, num_data * sizeof(double));
396:
397: /* fill in additional parts of tmp_C, tmp_d */
398:
399: for (i = 0; i < num_params; i++) {
400: /* fill in low diag. of tmp_C ... */
401: tmp_C[num_data + i][i] = *lambda;
402: /* ... and low part of tmp_d */
403: tmp_d[num_data + i] = 0;
404: }
405: /* printmatrix(tmp_C, num_data+num_params, num_params); */
406:
407: /* FIXME: residues[] isn't used at all. Why? Should it be used? */
408:
409: Givens(tmp_C, tmp_d, da, residues, num_params + num_data, num_params, 1);
410: /*print_matrix_and_vectors (tmp_C, tmp_d, residues,
411: num_params+num_data, num_params); */
412:
413: /* check if trial did ameliorate sum of squares */
414:
415: for (j = 0; j < num_params; j++)
416: temp_a[j] = a[j] + da[j];
417:
418: if (!analyze(temp_a, tmp_C, tmp_d, &tmp_chisq)) {
419: /* FIXME: will never be reached: always returns TRUE */
420: return ERROR;
421: }
422:
423: if (tmp_chisq < *chisq) { /* Success, accept new solution */
424: if (*lambda > MIN_LAMBDA) {
425: (void) putc('/', stderr);
426: *lambda /= lambda_down_factor;
427: }
428: *chisq = tmp_chisq;
429: for (j = 0; j < num_data; j++) {
430: memcpy(C[j], tmp_C[j], num_params * sizeof(double));
431: d[j] = tmp_d[j];
432: }
433: for (j = 0; j < num_params; j++)
434: a[j] = temp_a[j];
435: return BETTER;
436: } else { /* failure, increase lambda and return */
437: (void) putc('*', stderr);
438: *lambda *= lambda_up_factor;
439: return WORSE;
440: }
441: }
442:
443:
444: /* FIXME: in the new code, this function doesn't really do enough to be
445: * useful. Maybe it ought to be deleted, i.e. integrated with
446: * calculate() ?
447: */
448: /*****************************************************************
449: compute chi-square and numeric derivations
450: *****************************************************************/
451: static TBOOLEAN analyze(a, C, d, chisq)
452: double a[];
453: double **C;
454: double d[];
455: double *chisq;
456: {
457: /*
458: * used by marquardt to evaluate the linearized fitting matrix C
459: * and vector d, fills in only the top part of C and d
460: * I don't use a temporary array zfunc[] any more. Just use
461: * d[] instead.
462: */
463: int i, j;
464:
465: *chisq = 0;
466: calculate(d, C, a);
467:
468: for (i = 0; i < num_data; i++) {
469: /* note: order reversed, as used by Schwarz */
470: d[i] = (d[i] - fit_z[i]) / err_data[i];
471: *chisq += d[i] * d[i];
472: for (j = 0; j < num_params; j++)
473: C[i][j] /= err_data[i];
474: }
475: /* FIXME: why return a value that is always TRUE ? */
476: return TRUE;
477: }
478:
479:
480: /* To use the more exact, but slower two-side formula, activate the
481: following line: */
482: /*#define TWO_SIDE_DIFFERENTIATION */
483: /*****************************************************************
484: compute function values and partial derivatives of chi-square
485: *****************************************************************/
486: static void calculate(zfunc, dzda, a)
487: double *zfunc;
488: double **dzda;
489: double a[];
490: {
491: int k, p;
492: double tmp_a;
493: double *tmp_high, *tmp_pars;
494: #ifdef TWO_SIDE_DIFFERENTIATION
495: double *tmp_low;
496: #endif
497:
498: tmp_high = vec(num_data); /* numeric derivations */
499: #ifdef TWO_SIDE_DIFFERENTIATION
500: tmp_low = vec(num_data);
501: #endif
502: tmp_pars = vec(num_params);
503:
504: /* first function values */
505:
506: call_gnuplot(a, zfunc);
507:
508: /* then derivatives */
509:
510: for (p = 0; p < num_params; p++)
511: tmp_pars[p] = a[p];
512: for (p = 0; p < num_params; p++) {
513: tmp_a = fabs(a[p]) < NEARLY_ZERO ? NEARLY_ZERO : a[p];
514: tmp_pars[p] = tmp_a * (1 + DELTA);
515: call_gnuplot(tmp_pars, tmp_high);
516: #ifdef TWO_SIDE_DIFFERENTIATION
517: tmp_pars[p] = tmp_a * (1 - DELTA);
518: call_gnuplot(tmp_pars, tmp_low);
519: #endif
520: for (k = 0; k < num_data; k++)
521: #ifdef TWO_SIDE_DIFFERENTIATION
522: dzda[k][p] = (tmp_high[k] - tmp_low[k]) / (2 * tmp_a * DELTA);
523: #else
524: dzda[k][p] = (tmp_high[k] - zfunc[k]) / (tmp_a * DELTA);
525: #endif
526: tmp_pars[p] = a[p];
527: }
528:
529: #ifdef TWO_SIDE_DIFFERENTIATION
530: free(tmp_low);
531: #endif
532: free(tmp_high);
533: free(tmp_pars);
534: }
535:
536:
537: /*****************************************************************
538: call internal gnuplot functions
539: *****************************************************************/
540: static void call_gnuplot(par, data)
541: double *par;
542: double *data;
543: {
544: int i;
545: struct value v;
546:
547: /* set parameters first */
548: for (i = 0; i < num_params; i++) {
549: (void) Gcomplex(&v, par[i], 0.0);
550: setvar(par_name[i], v);
551: }
552:
553: for (i = 0; i < num_data; i++) {
554: /* calculate fit-function value */
555: (void) Gcomplex(&func.dummy_values[0], fit_x[i], 0.0);
556: (void) Gcomplex(&func.dummy_values[1], fit_y[i], 0.0);
557: evaluate_at(func.at, &v);
558: if (undefined)
559: Eex("Undefined value during function evaluation");
560: data[i] = real(&v);
561: }
562: }
563:
564:
565: /*****************************************************************
566: handle user interrupts during fit
567: *****************************************************************/
568: static TBOOLEAN fit_interrupt()
569: {
570: while (TRUE) {
571: fputs("\n\n(S)top fit, (C)ontinue, (E)xecute FIT_SCRIPT: ", STANDARD);
572: switch (getc(stdin)) {
573:
574: case EOF:
575: case 's':
576: case 'S':
577: fputs("Stop.", STANDARD);
578: user_stop = TRUE;
579: return FALSE;
580:
581: case 'c':
582: case 'C':
583: fputs("Continue.", STANDARD);
584: return TRUE;
585:
586: case 'e':
587: case 'E':{
588: int i;
589: struct value v;
590: char *tmp;
591:
592: tmp = (fit_script != 0 && *fit_script) ? fit_script : DEFAULT_CMD;
593: fprintf(STANDARD, "executing: %s", tmp);
594: /* set parameters visible to gnuplot */
595: for (i = 0; i < num_params; i++) {
596: (void) Gcomplex(&v, a[i], 0.0);
597: setvar(par_name[i], v);
598: }
599: sprintf(input_line, tmp);
600: (void) do_line();
601: }
602: }
603: }
604: return TRUE;
605: }
606:
607:
608: /*****************************************************************
609: frame routine for the marquardt-fit
610: *****************************************************************/
611: static TBOOLEAN regress(a)
612: double a[];
613: {
614: double **covar, *dpar, **C, chisq, last_chisq, lambda;
615: int iter, i, j;
616: marq_res_t res;
617:
618: chisq = last_chisq = INFINITY;
619: C = matr(num_data + num_params, num_params);
620: lambda = -1; /* use sign as flag */
621: iter = 0; /* iteration counter */
622:
623: /* ctrlc now serves as Hotkey */
624: ctrlc_setup();
625:
626: /* Initialize internal variables and 1st chi-square check */
627: if ((res = marquardt(a, C, &chisq, &lambda)) == ERROR)
628: Eex("FIT: error occured during fit");
629: res = BETTER;
630:
631: show_fit(iter, chisq, chisq, a, lambda, STANDARD);
632: show_fit(iter, chisq, chisq, a, lambda, log_f);
633:
634: /* MAIN FIT LOOP: do the regression iteration */
635:
636: /* HBB 981118: initialize new variable 'user_break' */
637: user_stop = FALSE;
638:
639: do {
640: /*
641: * MSDOS defines signal(SIGINT) but doesn't handle it through
642: * real interrupts. So there remain cases in which a ctrl-c may
643: * be uncaught by signal. We must use kbhit() instead that really
644: * serves the keyboard interrupt (or write an own interrupt func
645: * which also generates #ifdefs)
646: *
647: * I hope that other OSes do it better, if not... add #ifdefs :-(
648: * EMX does not have kbhit.
649: *
650: * HBB: I think this can be enabled for DJGPP V2. SIGINT is actually
651: * handled there, AFAIK.
652: */
653: #if ((defined(MSDOS) || defined(DOS386)) && !defined(__EMX__)) || defined(ATARI) || defined(MTOS)
654: if (kbhit()) {
655: do {
656: getchx();
657: } while (kbhit());
658: ctrlc_flag = TRUE;
659: }
660: #endif
661:
662: if (ctrlc_flag) {
663: show_fit(iter, chisq, last_chisq, a, lambda, STANDARD);
664: ctrlc_flag = FALSE;
665: if (!fit_interrupt()) /* handle keys */
666: break;
667: }
668: if (res == BETTER) {
669: iter++;
670: last_chisq = chisq;
671: }
672: if ((res = marquardt(a, C, &chisq, &lambda)) == BETTER)
673: show_fit(iter, chisq, last_chisq, a, lambda, STANDARD);
674: } while ((res != ERROR)
675: && (lambda < MAX_LAMBDA)
676: && ((maxiter == 0) || (iter <= maxiter))
677: && (res == WORSE
678: || ((chisq > NEARLY_ZERO)
679: ? ((last_chisq - chisq) / chisq)
680: : (last_chisq - chisq)) > epsilon
681: )
682: );
683:
684: /* fit done */
685:
686: #if !defined(ATARI) && !defined(MTOS)
687: /* restore original SIGINT function */
688: interrupt_setup();
689: #endif
690:
691: /* HBB 970304: the maxiter patch: */
692: if ((maxiter > 0) && (iter > maxiter)) {
693: Dblf2("\nMaximum iteration count (%d) reached. Fit stopped.\n", maxiter);
694: } else if (user_stop) {
695: Dblf2("\nThe fit was stopped by the user after %d iterations.\n", iter);
696: } else {
697: Dblf2("\nAfter %d iterations the fit converged.\n", iter);
698: }
699:
700: Dblf2("final sum of squares of residuals : %g\n", chisq);
701: if (chisq > NEARLY_ZERO) {
702: Dblf2("rel. change during last iteration : %g\n\n", (chisq - last_chisq) / chisq);
703: } else {
704: Dblf2("abs. change during last iteration : %g\n\n", (chisq - last_chisq));
705: }
706:
707: if (res == ERROR)
708: Eex("FIT: error occured during fit");
709:
710: /* compute errors in the parameters */
711:
712: if (num_data == num_params) {
713: int i;
714:
715: Dblf("\nExactly as many data points as there are parameters.\n");
716: Dblf("In this degenerate case, all errors are zero by definition.\n\n");
717: Dblf("Final set of parameters \n");
718: Dblf("======================= \n\n");
719: for (i = 0; i < num_params; i++)
720: Dblf3("%-15.15s = %-15g\n", par_name[i], a[i]);
721: } else if (chisq < NEARLY_ZERO) {
722: int i;
723:
724: Dblf("\nHmmmm.... Sum of squared residuals is zero. Can't compute errors.\n\n");
725: Dblf("Final set of parameters \n");
726: Dblf("======================= \n\n");
727: for (i = 0; i < num_params; i++)
728: Dblf3("%-15.15s = %-15g\n", par_name[i], a[i]);
729: } else {
1.1.1.2 ! maekawa 730: Dblf2("degrees of freedom (ndf) : %d\n", num_data - num_params);
! 731: Dblf2("rms of residuals (stdfit) = sqrt(WSSR/ndf) : %g\n", sqrt(chisq / (num_data - num_params)));
! 732: Dblf2("variance of residuals (reduced chisquare) = WSSR/ndf : %g\n\n", chisq / (num_data - num_params));
1.1 maekawa 733:
734: /* get covariance-, Korrelations- and Kurvature-Matrix */
735: /* and errors in the parameters */
736:
737: /* compute covar[][] directly from C */
738: Givens(C, 0, 0, 0, num_data, num_params, 0);
739: /*printmatrix(C, num_params, num_params); */
740:
741: /* Use lower square of C for covar */
742: covar = C + num_data;
743: Invert_RtR(C, covar, num_params);
744: /*printmatrix(covar, num_params, num_params); */
745:
746: /* calculate unscaled parameter errors in dpar[]: */
747: dpar = vec(num_params);
748: for (i = 0; i < num_params; i++) {
749: /* FIXME: can this still happen ? */
750: if (covar[i][i] <= 0.0) /* HBB: prevent floating point exception later on */
751: Eex("Calculation error: non-positive diagonal element in covar. matrix");
752: dpar[i] = sqrt(covar[i][i]);
753: }
754:
755: /* transform covariances into correlations */
756: for (i = 0; i < num_params; i++) {
757: /* only lower triangle needs to be handled */
758: for (j = 0; j <= i; j++)
759: covar[i][j] /= dpar[i] * dpar[j];
760: }
761:
762: /* scale parameter errors based on chisq */
763: chisq = sqrt(chisq / (num_data - num_params));
764: for (i = 0; i < num_params; i++)
765: dpar[i] *= chisq;
766:
767: Dblf("Final set of parameters Asymptotic Standard Error\n");
768: Dblf("======================= ==========================\n\n");
769:
770: for (i = 0; i < num_params; i++) {
771: double temp =
772: (fabs(a[i]) < NEARLY_ZERO) ? 0.0 : fabs(100.0 * dpar[i] / a[i]);
773: Dblf6("%-15.15s = %-15g %-3.3s %-12.4g (%.4g%%)\n",
774: par_name[i], a[i], PLUSMINUS, dpar[i], temp);
775: }
776:
777: Dblf("\n\ncorrelation matrix of the fit parameters:\n\n");
778: Dblf(" ");
779:
780: for (j = 0; j < num_params; j++)
781: Dblf2("%-6.6s ", par_name[j]);
782:
783: Dblf("\n");
784: for (i = 0; i < num_params; i++) {
785: Dblf2("%-15.15s", par_name[i]);
786: for (j = 0; j <= i; j++) {
787: /* Only print lower triangle of symmetric matrix */
788: Dblf2("%6.3f ", covar[i][j]);
789: }
790: Dblf("\n");
791: }
792:
793: free(dpar);
794: }
795:
1.1.1.2 ! maekawa 796: /* HBB 990220: re-imported this snippet from older versions. Finally,
! 797: * some user noticed that it *is* necessary, after all. Not even
! 798: * Carsten Grammes himself remembered what it was for... :-(
! 799: * The thing is: the value of the last parameter is not reset to
! 800: * its original one after the derivatives have been calculated
! 801: */
! 802: /* restore last parameter's value (not done by calculate) */
! 803: {
! 804: struct value val;
! 805: Gcomplex (&val, a[num_params-1], 0.0);
! 806: setvar (par_name[num_params-1], val);
! 807: }
! 808:
1.1 maekawa 809: /* call destructor for allocated vars */
810: lambda = -2; /* flag value, meaning 'destruct!' */
811: (void) marquardt(a, C, &chisq, &lambda);
812:
813: free_matr(C);
814: return TRUE;
815: }
816:
817:
818: /*****************************************************************
819: display actual state of the fit
820: *****************************************************************/
821: static void show_fit(i, chisq, last_chisq, a, lambda, device)
822: int i;
823: double chisq;
824: double last_chisq;
825: double *a;
826: double lambda;
827: FILE *device;
828: {
829: int k;
830:
831: fprintf(device, "\n\n\
832: Iteration %d\n\
833: WSSR : %-15g delta(WSSR)/WSSR : %g\n\
834: delta(WSSR) : %-15g limit for stopping : %g\n\
835: lambda : %g\n\n%s parameter values\n\n",
836: i, chisq, chisq > NEARLY_ZERO ? (chisq - last_chisq) / chisq : 0.0,
837: chisq - last_chisq, epsilon, lambda,
838: (i > 0 ? "resultant" : "initial set of free"));
839: for (k = 0; k < num_params; k++)
840: fprintf(device, "%-15.15s = %g\n", par_name[k], a[k]);
841: }
842:
843:
844:
845: /*****************************************************************
846: is_empty: check for valid string entries
847: *****************************************************************/
848: static TBOOLEAN is_empty(s)
849: char *s;
850: {
851: while (*s == ' ' || *s == '\t' || *s == '\n')
852: s++;
853: return (TBOOLEAN) (*s == '#' || *s == '\0');
854: }
855:
856:
857: /*****************************************************************
858: get next word of a multi-word string, advance pointer
859: *****************************************************************/
860: char *get_next_word(s, subst)
861: char **s;
862: char *subst;
863: {
864: char *tmp = *s;
865:
866: while (*tmp == ' ' || *tmp == '\t' || *tmp == '=')
867: tmp++;
868: if (*tmp == '\n' || *tmp == '\0') /* not found */
869: return NULL;
870: if ((*s = strpbrk(tmp, " =\t\n")) == NULL)
871: *s = tmp + strlen(tmp);
872: *subst = **s;
873: *(*s)++ = '\0';
874: return tmp;
875: }
876:
877:
878: /*****************************************************************
879: check for variable identifiers
880: *****************************************************************/
881: static TBOOLEAN is_variable(s)
882: char *s;
883: {
884: while (*s != '\0') {
885: if (!isalnum((int) *s) && *s != '_')
886: return FALSE;
887: s++;
888: }
889: return TRUE;
890: }
891:
892: /*****************************************************************
893: first time settings
894: *****************************************************************/
895: void init_fit()
896: {
897: func.at = (struct at_type *) NULL; /* need to parse 1 time */
898: }
899:
900:
901: /*****************************************************************
902: Set a GNUPLOT user-defined variable
903: ******************************************************************/
904:
905: void setvar(varname, data)
906: char *varname;
907: struct value data;
908: {
909: register struct udvt_entry *udv_ptr = first_udv, *last = first_udv;
910:
911: /* check if it's already in the table... */
912:
913: while (udv_ptr) {
914: last = udv_ptr;
915: if (!strcmp(varname, udv_ptr->udv_name))
916: break;
917: udv_ptr = udv_ptr->next_udv;
918: }
919:
920: if (!udv_ptr) { /* generate new entry */
921: last->next_udv = udv_ptr = (struct udvt_entry *)
1.1.1.2 ! maekawa 922: gp_alloc(sizeof(struct udvt_entry), "fit setvar");
1.1 maekawa 923: udv_ptr->next_udv = NULL;
924: }
925: safe_strncpy(udv_ptr->udv_name, varname, sizeof(udv_ptr->udv_name));
926: udv_ptr->udv_value = data;
927: udv_ptr->udv_undef = FALSE;
928: }
929:
930:
931:
932: /*****************************************************************
933: Read INTGR Variable value, return 0 if undefined or wrong type
934: *****************************************************************/
935: int getivar(varname)
936: char *varname;
937: {
938: register struct udvt_entry *udv_ptr = first_udv;
939:
940: while (udv_ptr) {
941: if (!strcmp(varname, udv_ptr->udv_name))
942: return udv_ptr->udv_value.type == INTGR
943: ? udv_ptr->udv_value.v.int_val /* valid */
944: : 0; /* wrong type */
945: udv_ptr = udv_ptr->next_udv;
946: }
947: return 0; /* not in table */
948: }
949:
950:
951: /*****************************************************************
952: Read DOUBLE Variable value, return 0 if undefined or wrong type
953: I dont think it's a problem that it's an integer - div
954: *****************************************************************/
955: static double getdvar(varname)
956: char *varname;
957: {
958: register struct udvt_entry *udv_ptr = first_udv;
959:
960: for (; udv_ptr; udv_ptr = udv_ptr->next_udv)
961: if (strcmp(varname, udv_ptr->udv_name) == 0)
962: return real(&(udv_ptr->udv_value));
963:
964: /* get here => not found */
965: return 0;
966: }
967:
968: /*****************************************************************
969: like getdvar, but
970: - convert it from integer to real if necessary
971: - create it with value INITIAL_VALUE if not found or undefined
972: *****************************************************************/
973: static double createdvar(varname, value)
974: char *varname;
975: double value;
976: {
977: register struct udvt_entry *udv_ptr = first_udv;
978:
979: for (; udv_ptr; udv_ptr = udv_ptr->next_udv)
980: if (strcmp(varname, udv_ptr->udv_name) == 0) {
981: if (udv_ptr->udv_undef) {
982: udv_ptr->udv_undef = 0;
983: (void) Gcomplex(&udv_ptr->udv_value, value, 0.0);
984: } else if (udv_ptr->udv_value.type == INTGR) {
985: (void) Gcomplex(&udv_ptr->udv_value, (double) udv_ptr->udv_value.v.int_val, 0.0);
986: }
987: return real(&(udv_ptr->udv_value));
988: }
989: /* get here => not found */
990:
991: {
992: struct value tempval;
993: (void) Gcomplex(&tempval, value, 0.0);
994: setvar(varname, tempval);
995: }
996:
997: return value;
998: }
999:
1000:
1001: /*****************************************************************
1002: Split Identifier into path and filename
1003: *****************************************************************/
1004: static void splitpath(s, p, f)
1005: char *s;
1006: char *p;
1007: char *f;
1008: {
1009: register char *tmp;
1010: tmp = s + strlen(s) - 1;
1011: while (*tmp != '\\' && *tmp != '/' && *tmp != ':' && tmp - s >= 0)
1012: tmp--;
1013: strcpy(f, tmp + 1);
1014: safe_strncpy(p, s, (size_t) (tmp - s + 1));
1015: p[tmp - s + 1] = NUL;
1016: }
1017:
1018:
1019: /*****************************************************************
1020: write the actual parameters to start parameter file
1021: *****************************************************************/
1022: void update(pfile, npfile)
1023: char *pfile, *npfile;
1024: {
1025: char fnam[256], path[256], sstr[256], pname[64], tail[127], *s = sstr,
1026: *tmp, c;
1027: char ifilename[256], *ofilename;
1028: FILE *of, *nf;
1029: double pval;
1030:
1031:
1032: /* update pfile npfile:
1033: if npfile is a valid file name,
1034: take pfile as input file and
1035: npfile as output file
1036: */
1037: if (VALID_FILENAME(npfile)) {
1038: safe_strncpy(ifilename, pfile, sizeof(ifilename));
1039: ofilename = npfile;
1040: } else {
1041: #ifdef BACKUP_FILESYSTEM
1042: /* filesystem will keep original as previous version */
1043: safe_strncpy(ifilename, pfile, sizeof(ifilename));
1044: #else
1045: backup_file(ifilename, pfile); /* will Eex if it fails */
1046: #endif
1047: ofilename = pfile;
1048: }
1049:
1050: /* split into path and filename */
1051: splitpath(ifilename, path, fnam);
1052: if (!(of = fopen(ifilename, "r")))
1053: Eex2("parameter file %s could not be read", ifilename);
1054:
1055: if (!(nf = fopen(ofilename, "w")))
1056: Eex2("new parameter file %s could not be created", ofilename);
1057:
1058: while (fgets(s = sstr, sizeof(sstr), of) != NULL) {
1059:
1060: if (is_empty(s)) {
1061: fputs(s, nf); /* preserve comments */
1062: continue;
1063: }
1064: if ((tmp = strchr(s, '#')) != NULL) {
1065: safe_strncpy(tail, tmp, sizeof(tail));
1066: *tmp = NUL;
1067: } else
1068: strcpy(tail, "\n");
1069:
1070: tmp = get_next_word(&s, &c);
1071: if (!is_variable(tmp) || strlen(tmp) > MAX_ID_LEN) {
1072: (void) fclose(nf);
1073: (void) fclose(of);
1074: Eex2("syntax error in parameter file %s", fnam);
1075: }
1076: safe_strncpy(pname, tmp, sizeof(pname));
1077: /* next must be '=' */
1078: if (c != '=') {
1079: tmp = strchr(s, '=');
1080: if (tmp == NULL) {
1081: (void) fclose(nf);
1082: (void) fclose(of);
1083: Eex2("syntax error in parameter file %s", fnam);
1084: }
1085: s = tmp + 1;
1086: }
1087: tmp = get_next_word(&s, &c);
1088: if (!sscanf(tmp, "%lf", &pval)) {
1089: (void) fclose(nf);
1090: (void) fclose(of);
1091: Eex2("syntax error in parameter file %s", fnam);
1092: }
1093: if ((tmp = get_next_word(&s, &c)) != NULL) {
1094: (void) fclose(nf);
1095: (void) fclose(of);
1096: Eex2("syntax error in parameter file %s", fnam);
1097: }
1098: /* now modify */
1099:
1100: if ((pval = getdvar(pname)) == 0)
1101: pval = (double) getivar(pname);
1102:
1103: sprintf(sstr, "%g", pval);
1104: if (!strchr(sstr, '.') && !strchr(sstr, 'e'))
1105: strcat(sstr, ".0"); /* assure CMPLX-type */
1106:
1107: fprintf(nf, "%-15.15s = %-15.15s %s", pname, sstr, tail);
1108: }
1109:
1110: if (fclose(nf) || fclose(of))
1111: Eex("I/O error during update");
1112:
1113: }
1114:
1115:
1116: /*****************************************************************
1117: Backup a file by renaming it to something useful. Return
1118: the new name in tofile
1119: *****************************************************************/
1120: static void backup_file(tofile, fromfile)
1121: char *tofile;
1122: const char *fromfile;
1123: /* tofile must point to a char array[] or allocated data. See update() */
1124: {
1125: #if defined (WIN32) || defined(MSDOS) || defined(VMS)
1126: char *tmpn;
1127: #endif
1128:
1129: /* win32 needs to have two attempts at the rename, since it may
1130: * be running on win32s with msdos 8.3 names
1131: */
1132:
1133: /* first attempt, for all o/s other than MSDOS */
1134:
1135: #ifndef MSDOS
1136:
1137: strcpy(tofile, fromfile);
1138:
1139: #ifdef VMS
1140: /* replace all dots with _, since we will be adding the only
1141: * dot allowed in VMS names
1142: */
1143: while ((tmpn = strchr(tofile, '.')) != NULL)
1144: *tmpn = '_';
1145: #endif /*VMS */
1146:
1147: strcat(tofile, BACKUP_SUFFIX);
1148:
1149: if (rename(fromfile, tofile) == 0)
1150: return; /* hurrah */
1151: #endif
1152:
1153:
1154: #if defined (WIN32) || defined(MSDOS)
1155:
1156: /* first attempt for msdos. Second attempt for win32s */
1157:
1158: /* beware : strncpy is very dangerous since it does not guarantee
1159: * to terminate the string. Copy up to 8 characters. If exactly
1160: * chars were copied, the string is not terminated. If the
1161: * source string was shorter than 8 chars, no harm is done
1162: * (here) by writing to offset 8.
1163: */
1164: safe_strncpy(tofile, fromfile, 8);
1165: /* tofile[8] = NUL; */
1166:
1167: while ((tmpn = strchr(tofile, '.')) != NULL)
1168: *tmpn = '_';
1169:
1170: strcat(tofile, BACKUP_SUFFIX);
1171:
1172: if (rename(fromfile, tofile) == 0)
1173: return; /* success */
1174:
1175: #endif /* win32 || msdos */
1176:
1177: /* get here => rename failed. */
1178: Eex3("Could not rename file %s to %s", fromfile, tofile);
1179: }
1180:
1181:
1182: /*****************************************************************
1183: Interface to the classic gnuplot-software
1184: *****************************************************************/
1185:
1186: void do_fit()
1187: {
1188: TBOOLEAN autorange_x = 3, autorange_y = 3; /* yes */
1189: /* HBB 980401: new: z range specification */
1190: TBOOLEAN autorange_z = 3;
1191: double min_x, max_x; /* range to fit */
1192: double min_y, max_y; /* range to fit */
1193: /* HBB 980401: new: z range specification */
1194: double min_z, max_z; /* range to fit */
1195: int dummy_x = -1, dummy_y = -1; /* eg fit [u=...] [v=...] */
1196: /* HBB 981210: memorize position of possible third [ : ] spec: */
1197: int zrange_token = -1;
1198:
1199: int i;
1200: double v[4];
1201: double tmpd;
1202: time_t timer;
1203: int token1, token2, token3;
1204: char *tmp;
1205:
1206: /* first look for a restricted x fit range... */
1207:
1208: if (equals(c_token, "[")) {
1209: c_token++;
1210: if (isletter(c_token)) {
1211: if (equals(c_token + 1, "=")) {
1212: dummy_x = c_token;
1213: c_token += 2;
1214: }
1215: /* else parse it as a xmin expression */
1216: }
1217: autorange_x = load_range(FIRST_X_AXIS, &min_x, &max_x, autorange_x);
1218: if (!equals(c_token, "]"))
1219: int_error("']' expected", c_token);
1220: c_token++;
1221: }
1222: /* ... and y */
1223:
1224: if (equals(c_token, "[")) {
1225: c_token++;
1226: if (isletter(c_token)) {
1227: if (equals(c_token + 1, "=")) {
1228: dummy_y = c_token;
1229: c_token += 2;
1230: }
1231: /* else parse it as a ymin expression */
1232: }
1233: autorange_y = load_range(FIRST_Y_AXIS, &min_y, &max_y, autorange_y);
1234: if (!equals(c_token, "]"))
1235: int_error("']' expected", c_token);
1236: c_token++;
1237: }
1238: /* HBB 980401: new: allow restricting the z range as well */
1239: /* ... and z */
1240:
1241: if (equals(c_token, "[")) {
1242: zrange_token = c_token++;
1243: if (isletter(c_token))
1244: /* do *not* allow the z range being given with a variable name */
1245: int_error("Can't re-name dependent variable", c_token);
1246: autorange_z = load_range(FIRST_Z_AXIS, &min_z, &max_z, autorange_z);
1247: if (!equals(c_token, "]"))
1248: int_error("']' expected", c_token);
1249: c_token++;
1250: #if 0 /* HBB 981210: move this to a later point */
1251: } else {
1252: /* Just in case I muck up things below: make sure that the z
1253: * range is the same as the y range, if it didn't get specified
1254: */
1255: autorange_z = autorange_y;
1256: min_z = min_y;
1257: max_z = max_y;
1258: #endif
1259: }
1260:
1261:
1262: /* now compile the function */
1263:
1264: token1 = c_token;
1265:
1266: if (func.at) {
1267: free(func.at);
1268: func.at = NULL; /* in case perm_at() does int_error */
1269: }
1270: dummy_func = &func;
1271:
1272:
1273: /* set the dummy variable names */
1274:
1275: if (dummy_x >= 0)
1276: copy_str(c_dummy_var[0], dummy_x, MAX_ID_LEN);
1277: else
1278: strcpy(c_dummy_var[0], dummy_var[0]);
1279:
1280: if (dummy_y >= 0)
1281: copy_str(c_dummy_var[0], dummy_y, MAX_ID_LEN);
1282: else
1283: strcpy(c_dummy_var[1], dummy_var[1]);
1284:
1285: func.at = perm_at();
1286: dummy_func = NULL;
1287:
1288: token2 = c_token;
1289:
1290: /* use datafile module to parse the datafile and qualifiers */
1291:
1292: columns = df_open(4); /* up to 4 using specs allowed */
1293: if (columns == 1)
1294: int_error("Need 2 to 4 using specs", c_token);
1295:
1.1.1.2 ! maekawa 1296: /* The following patch was made by Remko Scharroo, 25-Mar-1999
! 1297: * We need to check if one of the columns is time data, like
! 1298: * in plot2d and plot3d */
! 1299:
! 1300: if (datatype[FIRST_X_AXIS] == TIME) {
! 1301: if (columns < 2)
! 1302: int_error("Need full using spec for x time data", c_token);
! 1303: df_timecol[0] = 1;
! 1304: }
! 1305: if (datatype[FIRST_Y_AXIS] == TIME) {
! 1306: if (columns < 1)
! 1307: int_error("Need using spec for y time data", c_token);
! 1308: df_timecol[1] = 1;
! 1309: }
! 1310: /* HBB 990326: added this check. Just in case some wants to fit
! 1311: * time/date data depending on two other variables ... */
! 1312: if (datatype[FIRST_Z_AXIS] == TIME) {
! 1313: if (columns < 4)
! 1314: int_error("Need full using spec for z time data", c_token);
! 1315: else
! 1316: df_timecol[2] = 1;
! 1317: }
! 1318: /* End of patch by Remko Scharroo */
! 1319:
1.1 maekawa 1320: /* HBB 980401: if this is a single-variable fit, we shouldn't have
1321: * allowed a variable name specifier for 'y': */
1322: if ((dummy_y >= 0) && (columns < 4))
1323: int_error("Can't re-name 'y' in a one-variable fit", dummy_y);
1324:
1325: /* HBB 981210: two range specs mean different things, depending
1326: * on wether this is a 2D or 3D fit */
1.1.1.2 ! maekawa 1327: if (columns < 4) {
! 1328: if (zrange_token != -1)
! 1329: int_error("Three range-specs not allowed in on-variable fit", zrange_token);
! 1330: else {
! 1331: /* 2D fit, 2 ranges: second range is for *z*, not y: */
! 1332: autorange_z = autorange_y;
! 1333: if (autorange_y & 1)
! 1334: min_z = min_y;
! 1335: if (autorange_y & 2)
! 1336: max_z = max_y;
! 1337: }
1.1 maekawa 1338: }
1339:
1340: /* defer actually reading the data until we have parsed the rest
1341: * of the line */
1342:
1343: token3 = c_token;
1344:
1345: tmpd = getdvar(FITLIMIT); /* get epsilon if given explicitly */
1346: if (tmpd < 1.0 && tmpd > 0.0)
1347: epsilon = tmpd;
1348:
1349: /* HBB 970304: maxiter patch */
1350: maxiter = getivar(FITMAXITER);
1351:
1352: /* get startup value for lambda, if given */
1353: tmpd = getdvar(FITSTARTLAMBDA);
1354:
1355: if (tmpd > 0.0) {
1356: startup_lambda = tmpd;
1357: printf("Lambda Start value set: %g\n", startup_lambda);
1358: }
1359:
1360: /* get lambda up/down factor, if given */
1361: tmpd = getdvar(FITLAMBDAFACTOR);
1362:
1363: if (tmpd > 0.0) {
1364: lambda_up_factor = lambda_down_factor = tmpd;
1365: printf("Lambda scaling factors reset: %g\n", lambda_up_factor);
1366: }
1367: *fit_script = NUL;
1368: if ((tmp = getenv(FITSCRIPT)) != NULL)
1369: strcpy(fit_script, tmp);
1370:
1371: tmp = getenv(GNUFITLOG); /* open logfile */
1372: if (tmp != NULL) {
1373: char *tmp2 = &tmp[strlen(tmp) - 1];
1374: if (*tmp2 == '/' || *tmp2 == '\\')
1375: sprintf(logfile, "%s%s", tmp, logfile);
1376: else
1377: strcpy(logfile, tmp);
1378: }
1379: if (!log_f && /* div */ !(log_f = fopen(logfile, "a")))
1380: Eex2("could not open log-file %s", logfile);
1381: fputs("\n\n*******************************************************************************\n", log_f);
1382: (void) time(&timer);
1383: fprintf(log_f, "%s\n\n", ctime(&timer));
1384: {
1385: char *line = NULL;
1386:
1387: m_capture(&line, token2, token3 - 1);
1388: fprintf(log_f, "FIT: data read from %s\n", line);
1389: free(line);
1390: }
1391:
1392: max_data = MAX_DATA;
1393: fit_x = vec(max_data); /* start with max. value */
1394: fit_y = vec(max_data);
1395: fit_z = vec(max_data);
1396:
1397: /* first read in experimental data */
1398:
1399: err_data = vec(max_data);
1400: num_data = 0;
1401:
1402: while ((i = df_readline(v, 4)) != EOF) {
1403: if (num_data >= max_data) {
1404: /* increase max_data by factor of 1.5 */
1405: max_data = (max_data * 3) / 2;
1406: if (0
1407: || !redim_vec(&fit_x, max_data)
1408: || !redim_vec(&fit_y, max_data)
1409: || !redim_vec(&fit_z, max_data)
1410: || !redim_vec(&err_data, max_data)
1411: ) {
1412: /* Some of the reallocations went bad: */
1413: df_close();
1414: Eex2("Out of memory in fit: too many datapoints (%d)?", max_data);
1415: } else {
1416: /* Just so we know that the routine is at work: */
1417: fprintf(STANDARD, "Max. number of data points scaled up to: %d\n",
1418: max_data);
1419: }
1420: }
1421: switch (i) {
1422: case DF_UNDEFINED:
1423: case DF_FIRST_BLANK:
1424: case DF_SECOND_BLANK:
1425: continue;
1426: case 0:
1427: Eex2("bad data on line %d of datafile", df_line_number);
1428: break;
1429: case 1: /* only z provided */
1430: v[2] = v[0];
1431: v[0] = (double) df_datum;
1432: break;
1433: case 2: /* x and z */
1434: v[2] = v[1];
1435: break;
1436:
1437: /* only if the explicitly asked for 4 columns do we
1438: * do a 3d fit. (We can get here if they didn't
1439: * specify a using spec, and the file has 4 columns
1440: */
1441: case 4: /* x, y, z, error */
1442: if (columns == 4)
1443: break;
1444: /* else fall through */
1445: case 3: /* x, z, error */
1446: v[3] = v[2]; /* error */
1447: v[2] = v[1]; /* z */
1448: break;
1449:
1450: }
1451:
1452: /* skip this point if it is out of range */
1453: if (!(autorange_x & 1) && (v[0] < min_x))
1454: continue;
1455: if (!(autorange_x & 2) && (v[0] > max_x))
1456: continue;
1457: /* HBB 980401: yrange is only relevant for 3d-fits! */
1458: if (columns == 4) {
1459: if (!(autorange_y & 1) && (v[1] < min_y))
1460: continue;
1461: if (!(autorange_y & 2) && (v[1] > max_y))
1462: continue;
1463: }
1464: /* HBB 980401: check *z* range for all fits */
1465: if (!(autorange_z & 1) && (v[2] < min_z))
1466: continue;
1467: if (!(autorange_z & 2) && (v[2] > max_z))
1468: continue;
1469:
1470: fit_x[num_data] = v[0];
1471: fit_y[num_data] = v[1];
1472: fit_z[num_data] = v[2];
1473:
1474: /* we only use error if _explicitly_ asked for by a
1475: * using spec
1476: */
1477: err_data[num_data++] = (columns > 2) ? v[3] : 1;
1478: }
1479: df_close();
1480:
1481: if (num_data <= 1)
1482: Eex("No data to fit ");
1483:
1484: /* now resize fields to actual length: */
1485: redim_vec(&fit_x, num_data);
1486: redim_vec(&fit_y, num_data);
1487: redim_vec(&fit_z, num_data);
1488: redim_vec(&err_data, num_data);
1489:
1490: fprintf(log_f, " #datapoints = %d\n", num_data);
1491:
1492: if (columns < 3)
1493: fputs(" residuals are weighted equally (unit weight)\n\n", log_f);
1494:
1495: {
1496: char *line = NULL;
1497:
1498: m_capture(&line, token1, token2 - 1);
1499: fprintf(log_f, "function used for fitting: %s\n", line);
1500: free(line);
1501: }
1502:
1503: /* read in parameters */
1504:
1505: max_params = MAX_PARAMS; /* HBB 971023: make this resizeable */
1506:
1507: if (!equals(c_token, "via"))
1508: int_error("Need via and either parameter list or file", c_token);
1509:
1510: a = vec(max_params);
1511: par_name = (fixstr *) gp_alloc((max_params + 1) * sizeof(fixstr), "fit param");
1512: num_params = 0;
1513:
1514: if (isstring(++c_token)) { /* It's a parameter *file* */
1515: TBOOLEAN fixed;
1516: double tmp_par;
1517: char c, *s;
1.1.1.2 ! maekawa 1518: char sstr[MAX_LINE_LEN+1];
1.1 maekawa 1519: FILE *f;
1520:
1521: quote_str(sstr, c_token++, MAX_LINE_LEN);
1522:
1523: /* get parameters and values out of file and ignore fixed ones */
1524:
1525: fprintf(log_f, "fitted parameters and initial values from file: %s\n\n", sstr);
1526: if (!(f = fopen(sstr, "r")))
1527: Eex2("could not read parameter-file %s", sstr);
1528: while (TRUE) {
1529: if (!fgets(s = sstr, (int) sizeof(sstr), f)) /* EOF found */
1530: break;
1531: if ((tmp = strstr(s, FIXED)) != NULL) { /* ignore fixed params */
1532: *tmp = NUL;
1533: fprintf(log_f, "FIXED: %s\n", s);
1534: fixed = TRUE;
1535: } else
1536: fixed = FALSE;
1537: if ((tmp = strchr(s, '#')) != NULL)
1538: *tmp = NUL;
1539: if (is_empty(s))
1540: continue;
1541: tmp = get_next_word(&s, &c);
1542: if (!is_variable(tmp) || strlen(tmp) > MAX_ID_LEN) {
1543: (void) fclose(f);
1544: Eex("syntax error in parameter file");
1545: }
1546: safe_strncpy(par_name[num_params], tmp, sizeof(fixstr));
1547: /* next must be '=' */
1548: if (c != '=') {
1549: tmp = strchr(s, '=');
1550: if (tmp == NULL) {
1551: (void) fclose(f);
1552: Eex("syntax error in parameter file");
1553: }
1554: s = tmp + 1;
1555: }
1556: tmp = get_next_word(&s, &c);
1557: if (sscanf(tmp, "%lf", &tmp_par) != 1) {
1558: (void) fclose(f);
1559: Eex("syntax error in parameter file");
1560: }
1561: /* make fixed params visible to GNUPLOT */
1562: if (fixed) {
1563: struct value tempval;
1564: (void) Gcomplex(&tempval, tmp_par, 0.0);
1565: /* use parname as temp */
1566: setvar(par_name[num_params], tempval);
1567: } else {
1568: if (num_params >= max_params) {
1569: max_params = (max_params * 3) / 2;
1570: if (0
1571: || !redim_vec(&a, max_params)
1572: || !(par_name = gp_realloc(par_name, (max_params + 1) * sizeof(fixstr), "fit param resize"))
1573: ) {
1574: (void) fclose(f);
1575: Eex("Out of memory in fit: too many parameters?");
1576: }
1577: }
1578: a[num_params++] = tmp_par;
1579: }
1580:
1581: if ((tmp = get_next_word(&s, &c)) != NULL) {
1582: (void) fclose(f);
1583: Eex("syntax error in parameter file");
1584: }
1585: }
1586: (void) fclose(f);
1587:
1588: } else {
1589: /* not a string after via: it's a variable listing */
1590:
1591: fputs("fitted parameters initialized with current variable values\n\n", log_f);
1592: do {
1593: if (!isletter(c_token))
1594: Eex("no parameter specified");
1595: capture(par_name[num_params], c_token, c_token, (int) sizeof(par_name[0]));
1596: if (num_params >= max_params) {
1597: max_params = (max_params * 3) / 2;
1598: if (0
1599: || !redim_vec(&a, max_params)
1600: || !(par_name = gp_realloc(par_name, (max_params + 1) * sizeof(fixstr), "fit param resize"))
1601: ) {
1602: Eex("Out of memory in fit: too many parameters?");
1603: }
1604: }
1605: /* create variable if it doesn't exist */
1606: a[num_params] = createdvar(par_name[num_params], INITIAL_VALUE);
1607: ++num_params;
1608: } while (equals(++c_token, ",") && ++c_token);
1609: }
1610:
1611: redim_vec(&a, num_params);
1612: par_name = (fixstr *) gp_realloc(par_name, (num_params + 1) * sizeof(fixstr), "fit param");
1613:
1614: if (num_data < num_params)
1615: Eex("Number of data points smaller than number of parameters");
1616:
1617: /* avoid parameters being equal to zero */
1618: for (i = 0; i < num_params; i++)
1619: if (a[i] == 0)
1620: a[i] = NEARLY_ZERO;
1621:
1622: (void) regress(a);
1623:
1624: (void) fclose(log_f);
1625: log_f = NULL;
1626: free(fit_x);
1627: free(fit_y);
1628: free(fit_z);
1629: free(err_data);
1630: free(a);
1631: free(par_name);
1632: if (func.at) {
1633: free(func.at); /* release perm. action table */
1634: func.at = (struct at_type *) NULL;
1635: }
1636: safe_strncpy(last_fit_command, input_line, sizeof(last_fit_command));
1637: }
1638:
1639: #if defined(ATARI) || defined(MTOS)
1640: static int kbhit()
1641: {
1642: fd_set rfds;
1643: struct timeval timeout;
1644:
1645: timeout.tv_sec = 0;
1646: timeout.tv_usec = 0;
1647: rfds = (fd_set) (1L << fileno(stdin));
1648: return ((select(0, &rfds, NULL, NULL, &timeout) > 0) ? 1 : 0);
1649: }
1650: #endif
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>