Hardcoded patterns
[grace.git] / src / nonlwin.c
blob1809685b0277cf8bd00e5763deb6ffc480907ee8
1 /*
2 * Grace - GRaphing, Advanced Computation and Exploration of data
3 *
4 * Home page: http://plasma-gate.weizmann.ac.il/Grace/
5 *
6 * Copyright (c) 1991-1995 Paul J Turner, Portland, OR
7 * Copyright (c) 1996-2003 Grace Development Team
8 *
9 * Maintained by Evgeny Stambulchik
12 * All Rights Reserved
14 * This program is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 2 of the License, or
17 * (at your option) any later version.
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
24 * You should have received a copy of the GNU General Public License
25 * along with this program; if not, write to the Free Software
26 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
29 /*
31 * non linear curve fitting
35 #include <config.h>
37 #include <stdio.h>
38 #include <stdlib.h>
40 #include <Xm/Xm.h>
41 #include <Xm/ScrolledW.h>
43 #include "globals.h"
44 #include "defines.h"
45 #include "core_utils.h"
46 #include "utils.h"
47 #include "files.h"
48 #include "motifinc.h"
49 #include "xprotos.h"
51 /* nonlprefs.load possible values */
52 #define LOAD_VALUES 0
53 #define LOAD_RESIDUALS 1
54 #define LOAD_FUNCTION 2
56 #define WEIGHT_NONE 0
57 #define WEIGHT_Y 1
58 #define WEIGHT_Y2 2
59 #define WEIGHT_DY 3
60 #define WEIGHT_CUSTOM 4
62 /* prefs for non-linear fit */
63 typedef struct {
64 int autoload; /* do autoload */
65 int load; /* load to... */
66 int npoints; /* # of points to evaluate function at */
67 double start; /* start... */
68 double stop; /* stop ... */
69 } nonlprefs;
71 typedef struct {
72 TextStructure *formula_item;
73 Widget title_item;
74 Widget parm_item[MAXPARM];
75 Widget value_item[MAXPARM];
76 Widget constr_item[MAXPARM];
77 Widget lowb_item[MAXPARM];
78 Widget uppb_item[MAXPARM];
79 Widget tol_item;
80 OptionStructure *nparm_item;
81 SpinStructure *nsteps_item;
82 OptionStructure *load_item;
83 Widget autol_item;
84 Widget npts_item;
85 Widget start_item, stop_item;
86 Widget fload_rc;
87 RestrictionStructure *restr_item;
88 OptionStructure *weigh_item;
89 Widget wfunc_item;
90 } Nonl_ui;
92 typedef struct {
93 NLFit nlfit;
94 nonlprefs prefs;
95 int nsteps;
97 int autol;
99 int npts;
100 double start, stop;
102 int restr_type;
103 int restr_negate;
105 int weight_method;
106 char *wfunc;
107 } Nonl_pars;
109 static void do_nonl_toggle(OptionStructure *opt, int onoff, void *data);
110 static void nonl_wf_cb(OptionStructure *opt, int value, void *data);
111 static void do_constr_toggle(Widget tbut, int onoff, void *data);
113 static void update_nonl_frame(Nonl_ui *ui, NLFit *nlfit);
115 static void reset_frame_cb(Widget but, void *data);
117 static void do_nparm_toggle(OptionStructure *opt, int value, void *data);
118 static void create_openfit_popup(Widget but, void *data);
119 static void create_savefit_popup(Widget but, void *data);
120 static int do_openfit_proc(FSBStructure *fsb, char *filename, void *data);
121 static int do_savefit_proc(FSBStructure *fsb, char *filename, void *data);
123 static void do_nparm_toggle(OptionStructure *opt, int value, void *data)
125 Nonl_ui *ui = (Nonl_ui *) data;
126 int i;
127 for (i = 0; i < MAXPARM; i++) {
128 if (i < value) {
129 WidgetManage(ui->parm_item[i]);
130 } else {
131 WidgetUnmanage(ui->parm_item[i]);
136 static void reset_frame_cb(Widget but, void *data)
138 Nonl_ui *ui = (Nonl_ui *) data;
139 NLFit nlfit;
141 memset(&nlfit, 0, sizeof(NLFit));
143 reset_nonl(&nlfit);
145 update_nonl_frame(ui, &nlfit);
148 static void *nonl_build_cb(TransformStructure *tdialog)
150 Nonl_ui *ui;
152 ui = xmalloc(sizeof(Nonl_ui));
153 if (ui) {
154 int i;
155 char buf[256];
156 OptionItem np_option_items[MAXPARM + 1], option_items[5];
157 Widget frame, menubar, menupane;
158 Widget nonl_tab, nonl_main, nonl_advanced;
159 Widget sw, title_fr, fr3, rc1, rc2, rc3, lab;
161 frame = tdialog->frame;
163 menubar = tdialog->menubar;
165 menupane = CreateMenu(menubar, "File", 'F', FALSE);
166 CreateMenuButton(menupane, "Open...", 'O', create_openfit_popup, (void *) ui);
167 CreateMenuButton(menupane, "Save...", 'S', create_savefit_popup, (void *) ui);
168 CreateMenuSeparator(menupane);
169 CreateMenuButtonA(menupane, "Close", 'C', "<Key>Escape", "Esc", destroy_dialog_cb, frame);
171 menupane = CreateMenu(menubar, "Edit", 'E', FALSE);
173 CreateMenuButton(menupane, "Reset fit parameters", 'R', reset_frame_cb, (void *) ui);
175 menupane = CreateMenu(menubar, "View", 'V', FALSE);
177 ui->autol_item = CreateMenuToggle(menupane, "Autoload", 'A',
178 NULL, NULL);
180 menupane = CreateMenu(menubar, "Help", 'H', TRUE);
182 CreateMenuHelpButton(menupane, "On fit", 'f',
183 frame, "doc/UsersGuide.html#non-linear-fit");
185 WidgetManage(menubar);
187 title_fr = CreateFrame(frame, NULL);
188 XtVaSetValues(title_fr, XmNshadowType, XmSHADOW_ETCHED_OUT, NULL);
189 ui->title_item = CreateLabel(title_fr, NULL);
190 AddDialogFormChild(frame, title_fr);
192 /* ------------ Tabs --------------*/
194 nonl_tab = CreateTab(frame);
197 /* ------------ Main tab --------------*/
199 nonl_main = CreateTabPage(nonl_tab, "Main");
201 ui->formula_item = CreateText(nonl_main, "Formula:");
202 rc1 = CreateHContainer(nonl_main);
204 for (i = 0; i < MAXPARM + 1; i++) {
205 np_option_items[i].value = i;
206 sprintf(buf, "%d", i);
207 np_option_items[i].label = copy_string(NULL, buf);
209 ui->nparm_item = CreateOptionChoice(rc1,
210 "Parameters:", 1, MAXPARM + 1, np_option_items);
211 AddOptionChoiceCB(ui->nparm_item, do_nparm_toggle, ui);
213 ui->tol_item = CreateTextItem(rc1, 8, "Tolerance:");
215 ui->nsteps_item = CreateSpinChoice(rc1, "Iterations:", 3,
216 SPIN_TYPE_INT, 0.0, 500.0, 5.0);
217 SetSpinChoice(ui->nsteps_item, 5.0);
219 sw = XtVaCreateManagedWidget("sw",
220 xmScrolledWindowWidgetClass, nonl_main,
221 XmNheight, 180,
222 XmNscrollingPolicy, XmAUTOMATIC,
223 NULL);
225 rc2 = CreateVContainer(sw);
226 for (i = 0; i < MAXPARM; i++) {
227 ui->parm_item[i] = CreateHContainer(rc2);
228 WidgetUnmanage(ui->parm_item[i]);
229 sprintf(buf, "A%1d: ", i);
230 ui->value_item[i] = CreateTextItem(ui->parm_item[i], 10, buf);
232 ui->constr_item[i] = CreateToggleButton(ui->parm_item[i], "Bounds:");
233 AddToggleButtonCB(ui->constr_item[i], do_constr_toggle, (void *) i);
235 ui->lowb_item[i] = CreateTextItem(ui->parm_item[i], 6, "");
237 sprintf(buf, "< A%1d < ", i);
238 lab = CreateLabel(ui->parm_item[i], buf);
240 ui->uppb_item[i] = CreateTextItem(ui->parm_item[i], 6, "");
243 /* ------------ Advanced tab --------------*/
245 nonl_advanced = CreateTabPage(nonl_tab, "Advanced");
247 ui->restr_item =
248 CreateRestrictionChoice(nonl_advanced, "Source data filtering");
250 fr3 = CreateFrame(nonl_advanced, "Weighting");
251 rc3 = CreateHContainer(fr3);
252 option_items[0].value = WEIGHT_NONE;
253 option_items[0].label = "None";
254 option_items[1].value = WEIGHT_Y;
255 option_items[1].label = "1/Y";
256 option_items[2].value = WEIGHT_Y2;
257 option_items[2].label = "1/Y^2";
258 option_items[3].value = WEIGHT_DY;
259 option_items[3].label = "1/dY^2";
260 option_items[4].value = WEIGHT_CUSTOM;
261 option_items[4].label = "Custom";
262 ui->weigh_item = CreateOptionChoice(rc3, "Weights", 1, 5, option_items);
263 ui->wfunc_item = CreateTextItem(rc3, 30, "Function:");
264 AddOptionChoiceCB(ui->weigh_item, nonl_wf_cb, (void *) ui->wfunc_item);
266 fr3 = CreateFrame(nonl_advanced, "Load options");
267 rc3 = CreateVContainer(fr3);
268 option_items[0].value = LOAD_VALUES;
269 option_items[0].label = "Fitted values";
270 option_items[1].value = LOAD_RESIDUALS;
271 option_items[1].label = "Residuals";
272 option_items[2].value = LOAD_FUNCTION;
273 option_items[2].label = "Function";
274 ui->load_item = CreateOptionChoice(rc3, "Load", 1, 3, option_items);
275 ui->fload_rc = CreateHContainer(rc3);
276 ui->start_item = CreateTextItem(ui->fload_rc, 6, "Start load at:");
277 ui->stop_item = CreateTextItem(ui->fload_rc, 6, "Stop load at:");
278 ui->npts_item = CreateTextItem(ui->fload_rc, 4, "# of points:");
279 AddOptionChoiceCB(ui->load_item, do_nonl_toggle, (void *) ui->fload_rc);
281 /* defaults */
282 SetToggleButtonState(ui->autol_item, TRUE);
283 SetOptionChoice(ui->load_item, LOAD_VALUES);
284 WidgetSetSensitive(ui->fload_rc, FALSE);
285 WidgetSetSensitive(XtParent(ui->wfunc_item), FALSE);
286 xv_setstr(ui->start_item, "0.0");
287 xv_setstr(ui->stop_item, "1.0");
288 xv_setstr(ui->npts_item, "10");
291 return (void *) ui;
294 static void nonl_free_cb(void *tddata)
296 Nonl_pars *pars = (Nonl_pars *) tddata;
297 if (pars) {
298 xfree(pars->nlfit.title);
299 xfree(pars->nlfit.formula);
301 xfree(pars->wfunc);
302 xfree(pars);
306 static void *nonl_get_cb(void *gui)
308 Nonl_ui *ui = (Nonl_ui *) gui;
309 Nonl_pars *pars;
311 pars = xmalloc(sizeof(Nonl_pars));
312 if (pars) {
313 int i;
314 NLFit *nlfit = &pars->nlfit;
316 pars->nlfit.title = NULL;
317 nlfit->formula = TextGetString(ui->formula_item);
318 nlfit->tolerance = atof(xv_getstr(ui->tol_item));
319 nlfit->parnum = GetOptionChoice(ui->nparm_item);
321 pars->nsteps = (int) GetSpinChoice(ui->nsteps_item);
323 for (i = 0; i < nlfit->parnum; i++) {
324 char buf[256];
325 nonlparm *nlp = &nlfit->parms[i];
326 strcpy(buf, xv_getstr(ui->value_item[i]));
327 if (sscanf(buf, "%lf", &nlp->value) != 1) {
328 errmsg("Invalid input in parameter field");
329 nonl_free_cb(pars);
330 return NULL;
333 nlp->constr = GetToggleButtonState(ui->constr_item[i]);
334 if (nlp->constr) {
335 strcpy(buf, xv_getstr(ui->lowb_item[i]));
336 if (sscanf(buf, "%lf", &nlp->min) != 1) {
337 errmsg("Invalid input in low-bound field");
338 nonl_free_cb(pars);
339 return NULL;
341 strcpy(buf, xv_getstr(ui->uppb_item[i]));
342 if (sscanf(buf, "%lf", &nlp->max) != 1) {
343 errmsg("Invalid input in upper-bound field");
344 nonl_free_cb(pars);
345 return NULL;
347 if ((nlp->value < nlp->min) || (nlp->value > nlp->max)) {
348 errmsg("Initial values must be within bounds");
349 nonl_free_cb(pars);
350 return NULL;
355 pars->prefs.autoload = GetToggleButtonState(ui->autol_item);
356 pars->prefs.load = GetOptionChoice(ui->load_item);
358 if (pars->prefs.load == LOAD_FUNCTION) {
359 if (xv_evalexpr(ui->start_item, &pars->prefs.start) != RETURN_SUCCESS) {
360 errmsg("Invalid input in start field");
361 nonl_free_cb(pars);
362 return NULL;
364 if (xv_evalexpr(ui->stop_item, &pars->prefs.stop) != RETURN_SUCCESS) {
365 errmsg("Invalid input in stop field");
366 nonl_free_cb(pars);
367 return NULL;
369 if (xv_evalexpri(ui->npts_item, &pars->prefs.npoints) != RETURN_SUCCESS) {
370 errmsg("Invalid input in npoints field");
371 nonl_free_cb(pars);
372 return NULL;
374 if (pars->prefs.npoints <= 1) {
375 errmsg("Number of points must be > 1");
376 nonl_free_cb(pars);
377 return NULL;
382 for (i = 0; i < nlfit->parnum; i++) {
383 nonlparm *nlp = &nlfit->parms[i];
384 double *var;
386 var = define_parser_scalar(nlp->name);
387 if (var) {
388 *var = nlp->value;
392 pars->weight_method = GetOptionChoice(ui->weigh_item);
393 pars->restr_type = GetOptionChoice(ui->restr_item->r_sel);
394 pars->restr_negate = GetToggleButtonState(ui->restr_item->negate);
397 return (void *) pars;
400 static int nonl_run_cb(Quark *psrc, Quark *pdest, void *tddata)
402 int i, res;
403 Nonl_pars *pars = (Nonl_pars *) tddata;
405 if (pars->nsteps) {
406 int nlen, wlen;
407 double *ytmp, *warray;
408 char *rarray;
410 /* apply weigh function */
411 nlen = set_get_length(psrc);
412 switch (pars->weight_method) {
413 case WEIGHT_Y:
414 case WEIGHT_Y2:
415 ytmp = set_get_col(psrc, DATA_Y);
416 for (i = 0; i < nlen; i++) {
417 if (ytmp[i] == 0.0) {
418 errmsg("Divide by zero while calculating weights");
419 return RETURN_FAILURE;
422 warray = xmalloc(nlen*SIZEOF_DOUBLE);
423 if (warray == NULL) {
424 errmsg("xmalloc failed in nonl_run_cb()");
425 return RETURN_FAILURE;
427 for (i = 0; i < nlen; i++) {
428 if (pars->weight_method == WEIGHT_Y) {
429 warray[i] = 1/ytmp[i];
430 } else {
431 warray[i] = 1/(ytmp[i]*ytmp[i]);
434 break;
435 case WEIGHT_DY:
436 ytmp = set_get_col(psrc, DATA_Y1);
437 if (ytmp == NULL) {
438 errmsg("The set doesn't have dY data column");
439 return RETURN_FAILURE;
441 for (i = 0; i < nlen; i++) {
442 if (ytmp[i] == 0.0) {
443 errmsg("Divide by zero while calculating weights");
444 return RETURN_FAILURE;
447 warray = xmalloc(nlen*SIZEOF_DOUBLE);
448 if (warray == NULL) {
449 errmsg("xmalloc failed in nonl_run_cb()");
451 for (i = 0; i < nlen; i++) {
452 warray[i] = 1/(ytmp[i]*ytmp[i]);
454 break;
455 case WEIGHT_CUSTOM:
456 if (set_parser_setno(psrc) != RETURN_SUCCESS) {
457 errmsg("Bad set");
458 return RETURN_FAILURE;
461 if (v_scanner(pars->wfunc, &wlen, &warray) != RETURN_SUCCESS) {
462 errmsg("Error evaluating expression for weights");
463 return RETURN_FAILURE;
465 if (wlen != nlen) {
466 errmsg("The array of weights has different length");
467 xfree(warray);
468 return RETURN_FAILURE;
470 break;
471 default:
472 warray = NULL;
473 break;
476 /* Apply restrictions */
477 res = get_restriction_array(psrc,
478 NULL, pars->restr_negate, &rarray);
479 if (res != RETURN_SUCCESS) {
480 errmsg("Error in restriction evaluation");
481 xfree(warray);
482 return RETURN_FAILURE;
485 /* The fit itself! */
486 res = do_nonlfit(psrc, &pars->nlfit, warray, rarray, pars->nsteps);
488 /* Free temp arrays */
489 xfree(warray);
490 xfree(rarray);
492 if (res != RETURN_SUCCESS) {
493 errmsg("Fatal error in do_nonlfit()");
494 return RETURN_FAILURE;
497 /* update_nonl_frame(ui, &pars->nlfit); */
501 * Select & activate a set to load results to
503 if (pars->prefs.autoload) {
504 int npts = 0;
505 double delx, *xfit, *y, *yfit;
507 switch (pars->prefs.load) {
508 case LOAD_VALUES:
509 case LOAD_RESIDUALS:
510 npts = set_get_length(psrc);
511 set_set_length(pdest, npts);
512 copycol2(psrc, pdest, DATA_X);
513 break;
514 case LOAD_FUNCTION:
515 npts = pars->prefs.npoints;
517 set_set_length(pdest, npts);
519 delx = (pars->prefs.stop - pars->prefs.start)/(npts - 1);
520 xfit = getx(pdest);
521 for (i = 0; i < npts; i++) {
522 xfit[i] = pars->prefs.start + i * delx;
524 break;
527 set_set_comment(pdest, pars->nlfit.formula);
529 do_compute(pdest, pdest, NULL, pars->nlfit.formula);
531 if (pars->prefs.load == LOAD_RESIDUALS) { /* load residuals */
532 y = gety(psrc);
533 yfit = gety(pdest);
534 for (i = 0; i < npts; i++) {
535 yfit[i] -= y[i];
540 return RETURN_SUCCESS;
543 void create_nonl_frame(Widget but, void *data)
545 static TransformStructure *tdialog = NULL;
547 if (!tdialog) {
548 TD_CBProcs cbs;
549 cbs.build_cb = nonl_build_cb;
550 cbs.get_cb = nonl_get_cb;
551 cbs.free_cb = nonl_free_cb;
552 cbs.run_cb = nonl_run_cb;
554 tdialog = CreateTransformDialogForm(app_shell,
555 "Non-linear curve fitting", LIST_TYPE_SINGLE, TRUE, &cbs);
558 RaiseTransformationDialog(tdialog);
561 static void update_nonl_frame(Nonl_ui *ui, NLFit *nlfit)
563 int i;
565 if (ui) {
566 char buf[256];
567 LabelSetString(ui->title_item, nlfit->title);
569 * If I define only XmALIGNMENT_CENTER (default!) then it's ignored - bug in Motif???
571 XtVaSetValues(ui->title_item, XmNalignment, XmALIGNMENT_BEGINNING, NULL);
572 XtVaSetValues(ui->title_item, XmNalignment, XmALIGNMENT_CENTER, NULL);
574 TextSetString(ui->formula_item, nlfit->formula);
575 sprintf(buf, "%g", nlfit->tolerance);
576 xv_setstr(ui->tol_item, buf);
577 SetOptionChoice(ui->nparm_item, nlfit->parnum);
578 for (i = 0; i < MAXPARM; i++) {
579 nonlparm *nlp = &nlfit->parms[i];
580 sprintf(buf, "%g", nlp->value);
581 xv_setstr(ui->value_item[i], buf);
582 SetToggleButtonState(ui->constr_item[i], nlp->constr);
583 sprintf(buf, "%g", nlp->min);
584 xv_setstr(ui->lowb_item[i], buf);
585 WidgetSetSensitive(ui->lowb_item[i], nlp->constr);
586 sprintf(buf, "%g", nlp->max);
587 xv_setstr(ui->uppb_item[i], buf);
588 WidgetSetSensitive(ui->uppb_item[i], nlp->constr);
589 if (i < nlfit->parnum) {
590 if (!WidgetIsManaged (ui->parm_item[i])) {
591 WidgetManage(ui->parm_item[i]);
593 } else {
594 if (WidgetIsManaged (ui->parm_item[i])) {
595 WidgetUnmanage(ui->parm_item[i]);
602 static void nonl_wf_cb(OptionStructure *opt, int value, void *data)
604 Widget rc = XtParent((Widget) data);
606 if (value == WEIGHT_CUSTOM) {
607 WidgetSetSensitive(rc, True);
608 } else {
609 WidgetSetSensitive(rc, False);
613 static void do_nonl_toggle(OptionStructure *opt, int value, void *data)
615 Widget rc = (Widget) data;
617 if (value == LOAD_FUNCTION) {
618 WidgetSetSensitive(rc, True);
619 } else {
620 WidgetSetSensitive(rc, False);
624 static void do_constr_toggle(Widget tbut, int onoff, void *data)
626 #if 0
627 int value = (int) data;
628 if (onoff) {
629 WidgetSetSensitive(ui->lowb_item[value], True);
630 WidgetSetSensitive(ui->uppb_item[value], True);
631 nlfit.parms[value].constr = TRUE;
632 } else {
633 WidgetSetSensitive(ui->lowb_item[value], False);
634 WidgetSetSensitive(ui->uppb_item[value], False);
635 nlfit.parms[value].constr = FALSE;
637 #endif
641 static void create_openfit_popup(Widget but, void *data)
643 static FSBStructure *fsb = NULL;
645 set_wait_cursor();
647 if (fsb == NULL) {
648 fsb = CreateFSBDialog(app_shell, "Open fit parameter file");
649 AddFSBDialogCB(fsb, do_openfit_proc, NULL);
650 FSBDialogSetPattern(fsb, "*.fit");
651 WidgetManage(fsb->FSB);
654 DialogRaise(fsb->FSB);
656 unset_wait_cursor();
659 static int do_openfit_proc(FSBStructure *fsb, char *filename, void *data)
661 errwin("Not implemented yet");
663 return FALSE;
667 static void create_savefit_popup(Widget but, void *data)
669 static FSBStructure *fsb = NULL;
670 static Widget title_item = NULL;
672 set_wait_cursor();
674 if (fsb == NULL) {
675 Widget fr;
677 fsb = CreateFSBDialog(app_shell, "Save fit parameter file");
678 fr = CreateFrame(fsb->rc, NULL);
679 title_item = CreateTextItem(fr, 25, "Title: ");
680 AddFSBDialogCB(fsb, do_savefit_proc, (void *) title_item);
681 FSBDialogSetPattern(fsb, "*.fit");
682 WidgetManage(fsb->FSB);
685 /* xv_setstr(title_item, ui->nlfit.title); */
687 DialogRaise(fsb->FSB);
689 unset_wait_cursor();
692 static int do_savefit_proc(FSBStructure *fsb, char *filename, void *data)
694 FILE *pp;
695 /* Widget title_item = (Widget) data; */
697 pp = gapp_openw(gapp, filename);
698 if (pp != NULL) {
699 #if 0
700 nlfit.title = copy_string(nlfit.title, xv_getstr(title_item));
701 #endif
702 errwin("Not implemented yet");
703 /* FIXME */;
704 gapp_close(pp);
706 return TRUE;