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