Annotation of OpenXM_contrib/gnuplot/fit.c, Revision 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>