Switched to FFTW-3.
[grace.git] / src / nonlwin.c
blob2f7913b728f0fcd667c7762a3cc9d7fd2f484bfa
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 TextStructure *value_item[MAXPARM];
76 Widget constr_item[MAXPARM];
77 TextStructure *lowb_item[MAXPARM];
78 TextStructure *uppb_item[MAXPARM];
79 TextStructure *tol_item;
80 OptionStructure *nparm_item;
81 SpinStructure *nsteps_item;
82 OptionStructure *load_item;
83 Widget autol_item;
84 TextStructure *npts_item;
85 TextStructure *start_item, *stop_item;
86 Widget fload_rc;
87 RestrictionStructure *restr_item;
88 OptionStructure *weigh_item;
89 TextStructure *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 LabelOptionItem np_option_items[MAXPARM + 1], option_items[5];
157 Widget frame, menubar, menupane, rc;
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, tdialog->form);
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 rc = CreateVContainer(frame);
189 title_fr = CreateFrame(rc, NULL);
190 XtVaSetValues(title_fr, XmNshadowType, XmSHADOW_ETCHED_OUT, NULL);
191 ui->title_item = CreateLabel(title_fr, NULL);
192 XtVaSetValues(ui->title_item, XmNalignment, XmALIGNMENT_CENTER, NULL);
194 /* ------------ Tabs --------------*/
196 nonl_tab = CreateTab(rc);
199 /* ------------ Main tab --------------*/
201 nonl_main = CreateTabPage(nonl_tab, "Main");
203 ui->formula_item = CreateText(nonl_main, "Formula:");
204 rc1 = CreateHContainer(nonl_main);
206 for (i = 0; i < MAXPARM + 1; i++) {
207 np_option_items[i].value = i;
208 sprintf(buf, "%d", i);
209 np_option_items[i].label = copy_string(NULL, buf);
211 ui->nparm_item = CreateLabelOptionChoice(rc1,
212 "Parameters:", 1, MAXPARM + 1, np_option_items);
213 AddOptionChoiceCB(ui->nparm_item, do_nparm_toggle, ui);
215 ui->tol_item = CreateText2(rc1, "Tolerance:", 8);
217 ui->nsteps_item = CreateSpinChoice(rc1, "Iterations:", 3,
218 SPIN_TYPE_INT, 0.0, 500.0, 5.0);
219 SpinChoiceSetValue(ui->nsteps_item, 5.0);
221 sw = XtVaCreateManagedWidget("sw",
222 xmScrolledWindowWidgetClass, nonl_main,
223 XmNheight, 180,
224 XmNscrollingPolicy, XmAUTOMATIC,
225 NULL);
227 rc2 = CreateVContainer(sw);
228 for (i = 0; i < MAXPARM; i++) {
229 ui->parm_item[i] = CreateHContainer(rc2);
230 WidgetUnmanage(ui->parm_item[i]);
231 sprintf(buf, "A%1d: ", i);
232 ui->value_item[i] = CreateText2(ui->parm_item[i], buf, 10);
234 ui->constr_item[i] = CreateToggleButton(ui->parm_item[i], "Bounds:");
235 AddToggleButtonCB(ui->constr_item[i], do_constr_toggle, (void *) i);
237 ui->lowb_item[i] = CreateText2(ui->parm_item[i], "", 6);
239 sprintf(buf, "< A%1d < ", i);
240 lab = CreateLabel(ui->parm_item[i], buf);
242 ui->uppb_item[i] = CreateText2(ui->parm_item[i], "", 6);
245 /* ------------ Advanced tab --------------*/
247 nonl_advanced = CreateTabPage(nonl_tab, "Advanced");
249 ui->restr_item =
250 CreateRestrictionChoice(nonl_advanced, "Source data filtering");
252 fr3 = CreateFrame(nonl_advanced, "Weighting");
253 rc3 = CreateHContainer(fr3);
254 option_items[0].value = WEIGHT_NONE;
255 option_items[0].label = "None";
256 option_items[1].value = WEIGHT_Y;
257 option_items[1].label = "1/Y";
258 option_items[2].value = WEIGHT_Y2;
259 option_items[2].label = "1/Y^2";
260 option_items[3].value = WEIGHT_DY;
261 option_items[3].label = "1/dY^2";
262 option_items[4].value = WEIGHT_CUSTOM;
263 option_items[4].label = "Custom";
264 ui->weigh_item = CreateLabelOptionChoice(rc3, "Weights", 1, 5, option_items);
265 ui->wfunc_item = CreateText2(rc3, "Function:", 30);
266 AddOptionChoiceCB(ui->weigh_item, nonl_wf_cb, ui->wfunc_item);
268 fr3 = CreateFrame(nonl_advanced, "Load options");
269 rc3 = CreateVContainer(fr3);
270 option_items[0].value = LOAD_VALUES;
271 option_items[0].label = "Fitted values";
272 option_items[1].value = LOAD_RESIDUALS;
273 option_items[1].label = "Residuals";
274 option_items[2].value = LOAD_FUNCTION;
275 option_items[2].label = "Function";
276 ui->load_item = CreateLabelOptionChoice(rc3, "Load", 1, 3, option_items);
277 ui->fload_rc = CreateHContainer(rc3);
278 ui->start_item = CreateText2(ui->fload_rc, "Start load at:", 6);
279 ui->stop_item = CreateText2(ui->fload_rc, "Stop load at:", 6);
280 ui->npts_item = CreateText2(ui->fload_rc, "# of points:", 4);
281 AddOptionChoiceCB(ui->load_item, do_nonl_toggle, ui->fload_rc);
283 /* defaults */
284 ToggleButtonSetState(ui->autol_item, TRUE);
285 SetOptionChoice(ui->load_item, LOAD_VALUES);
286 WidgetSetSensitive(ui->fload_rc, FALSE);
287 WidgetSetSensitive(ui->wfunc_item->form, FALSE);
288 TextSetString(ui->start_item, "0.0");
289 TextSetString(ui->stop_item, "1.0");
290 TextSetString(ui->npts_item, "10");
293 return (void *) ui;
296 static void nonl_free_cb(void *tddata)
298 Nonl_pars *pars = (Nonl_pars *) tddata;
299 if (pars) {
300 xfree(pars->nlfit.title);
301 xfree(pars->nlfit.formula);
303 xfree(pars->wfunc);
304 xfree(pars);
308 static void *nonl_get_cb(void *gui)
310 Nonl_ui *ui = (Nonl_ui *) gui;
311 Nonl_pars *pars;
313 pars = xmalloc(sizeof(Nonl_pars));
314 if (pars) {
315 int i;
316 char *s;
317 NLFit *nlfit = &pars->nlfit;
319 pars->nlfit.title = NULL;
320 nlfit->formula = TextGetString(ui->formula_item);
321 s = TextGetString(ui->tol_item);
322 nlfit->tolerance = atof(s);
323 xfree(s);
324 nlfit->parnum = GetOptionChoice(ui->nparm_item);
326 pars->nsteps = (int) SpinChoiceGetValue(ui->nsteps_item);
328 for (i = 0; i < nlfit->parnum; i++) {
329 char buf[256];
330 nonlparm *nlp = &nlfit->parms[i];
331 s = TextGetString(ui->value_item[i]);
332 strcpy(buf, s);
333 xfree(s);
334 if (sscanf(buf, "%lf", &nlp->value) != 1) {
335 errmsg("Invalid input in parameter field");
336 nonl_free_cb(pars);
337 return NULL;
340 nlp->constr = ToggleButtonGetState(ui->constr_item[i]);
341 if (nlp->constr) {
342 s = TextGetString(ui->lowb_item[i]);
343 strcpy(buf, s);
344 xfree(s);
345 if (sscanf(buf, "%lf", &nlp->min) != 1) {
346 errmsg("Invalid input in low-bound field");
347 nonl_free_cb(pars);
348 return NULL;
350 s = TextGetString(ui->uppb_item[i]);
351 strcpy(buf, s);
352 xfree(s);
353 if (sscanf(buf, "%lf", &nlp->max) != 1) {
354 errmsg("Invalid input in upper-bound field");
355 nonl_free_cb(pars);
356 return NULL;
358 if ((nlp->value < nlp->min) || (nlp->value > nlp->max)) {
359 errmsg("Initial values must be within bounds");
360 nonl_free_cb(pars);
361 return NULL;
366 pars->prefs.autoload = ToggleButtonGetState(ui->autol_item);
367 pars->prefs.load = GetOptionChoice(ui->load_item);
369 if (pars->prefs.load == LOAD_FUNCTION) {
370 if (xv_evalexpr(ui->start_item, &pars->prefs.start) != RETURN_SUCCESS) {
371 errmsg("Invalid input in start field");
372 nonl_free_cb(pars);
373 return NULL;
375 if (xv_evalexpr(ui->stop_item, &pars->prefs.stop) != RETURN_SUCCESS) {
376 errmsg("Invalid input in stop field");
377 nonl_free_cb(pars);
378 return NULL;
380 if (xv_evalexpri(ui->npts_item, &pars->prefs.npoints) != RETURN_SUCCESS) {
381 errmsg("Invalid input in npoints field");
382 nonl_free_cb(pars);
383 return NULL;
385 if (pars->prefs.npoints <= 1) {
386 errmsg("Number of points must be > 1");
387 nonl_free_cb(pars);
388 return NULL;
393 for (i = 0; i < nlfit->parnum; i++) {
394 nonlparm *nlp = &nlfit->parms[i];
395 double *var;
397 var = define_parser_scalar(nlp->name);
398 if (var) {
399 *var = nlp->value;
403 pars->weight_method = GetOptionChoice(ui->weigh_item);
404 pars->restr_type = GetOptionChoice(ui->restr_item->r_sel);
405 pars->restr_negate = ToggleButtonGetState(ui->restr_item->negate);
408 return (void *) pars;
411 static int nonl_run_cb(Quark *psrc, Quark *pdest, void *tddata)
413 int i, res;
414 Nonl_pars *pars = (Nonl_pars *) tddata;
416 if (pars->nsteps) {
417 int nlen, wlen;
418 double *ytmp, *warray;
419 char *rarray;
421 /* apply weigh function */
422 nlen = set_get_length(psrc);
423 switch (pars->weight_method) {
424 case WEIGHT_Y:
425 case WEIGHT_Y2:
426 ytmp = set_get_col(psrc, DATA_Y);
427 for (i = 0; i < nlen; i++) {
428 if (ytmp[i] == 0.0) {
429 errmsg("Divide by zero while calculating weights");
430 return RETURN_FAILURE;
433 warray = xmalloc(nlen*SIZEOF_DOUBLE);
434 if (warray == NULL) {
435 errmsg("xmalloc failed in nonl_run_cb()");
436 return RETURN_FAILURE;
438 for (i = 0; i < nlen; i++) {
439 if (pars->weight_method == WEIGHT_Y) {
440 warray[i] = 1/ytmp[i];
441 } else {
442 warray[i] = 1/(ytmp[i]*ytmp[i]);
445 break;
446 case WEIGHT_DY:
447 ytmp = set_get_col(psrc, DATA_Y1);
448 if (ytmp == NULL) {
449 errmsg("The set doesn't have dY data column");
450 return RETURN_FAILURE;
452 for (i = 0; i < nlen; i++) {
453 if (ytmp[i] == 0.0) {
454 errmsg("Divide by zero while calculating weights");
455 return RETURN_FAILURE;
458 warray = xmalloc(nlen*SIZEOF_DOUBLE);
459 if (warray == NULL) {
460 errmsg("xmalloc failed in nonl_run_cb()");
462 for (i = 0; i < nlen; i++) {
463 warray[i] = 1/(ytmp[i]*ytmp[i]);
465 break;
466 case WEIGHT_CUSTOM:
467 if (set_parser_setno(psrc) != RETURN_SUCCESS) {
468 errmsg("Bad set");
469 return RETURN_FAILURE;
472 if (v_scanner(pars->wfunc, &wlen, &warray) != RETURN_SUCCESS) {
473 errmsg("Error evaluating expression for weights");
474 return RETURN_FAILURE;
476 if (wlen != nlen) {
477 errmsg("The array of weights has different length");
478 xfree(warray);
479 return RETURN_FAILURE;
481 break;
482 default:
483 warray = NULL;
484 break;
487 /* Apply restrictions */
488 res = get_restriction_array(psrc,
489 NULL, pars->restr_negate, &rarray);
490 if (res != RETURN_SUCCESS) {
491 errmsg("Error in restriction evaluation");
492 xfree(warray);
493 return RETURN_FAILURE;
496 /* The fit itself! */
497 res = do_nonlfit(psrc, &pars->nlfit, warray, rarray, pars->nsteps);
499 /* Free temp arrays */
500 xfree(warray);
501 xfree(rarray);
503 if (res != RETURN_SUCCESS) {
504 errmsg("Fatal error in do_nonlfit()");
505 return RETURN_FAILURE;
508 /* update_nonl_frame(ui, &pars->nlfit); */
512 * Select & activate a set to load results to
514 if (pars->prefs.autoload) {
515 int npts = 0;
516 double delx, *xfit, *y, *yfit;
518 switch (pars->prefs.load) {
519 case LOAD_VALUES:
520 case LOAD_RESIDUALS:
521 npts = set_get_length(psrc);
522 set_set_length(pdest, npts);
523 copycol2(psrc, pdest, DATA_X);
524 break;
525 case LOAD_FUNCTION:
526 npts = pars->prefs.npoints;
528 set_set_length(pdest, npts);
530 delx = (pars->prefs.stop - pars->prefs.start)/(npts - 1);
531 xfit = getx(pdest);
532 for (i = 0; i < npts; i++) {
533 xfit[i] = pars->prefs.start + i * delx;
535 break;
538 set_set_comment(pdest, pars->nlfit.formula);
540 do_compute(pdest, pdest, NULL, pars->nlfit.formula);
542 if (pars->prefs.load == LOAD_RESIDUALS) { /* load residuals */
543 y = gety(psrc);
544 yfit = gety(pdest);
545 for (i = 0; i < npts; i++) {
546 yfit[i] -= y[i];
551 return RETURN_SUCCESS;
554 void create_nonl_frame(Widget but, void *data)
556 static TransformStructure *tdialog = NULL;
558 if (!tdialog) {
559 TD_CBProcs cbs;
560 cbs.build_cb = nonl_build_cb;
561 cbs.get_cb = nonl_get_cb;
562 cbs.free_cb = nonl_free_cb;
563 cbs.run_cb = nonl_run_cb;
565 tdialog = CreateTransformDialogForm(app_shell,
566 "Non-linear curve fitting", LIST_TYPE_SINGLE, TRUE, &cbs);
569 RaiseTransformationDialog(tdialog);
572 static void update_nonl_frame(Nonl_ui *ui, NLFit *nlfit)
574 int i;
576 if (ui) {
577 char buf[256];
578 LabelSetString(ui->title_item, nlfit->title);
580 * If I define only XmALIGNMENT_CENTER (default!) then it's ignored - bug in Motif???
582 XtVaSetValues(ui->title_item, XmNalignment, XmALIGNMENT_BEGINNING, NULL);
583 XtVaSetValues(ui->title_item, XmNalignment, XmALIGNMENT_CENTER, NULL);
585 TextSetString(ui->formula_item, nlfit->formula);
586 sprintf(buf, "%g", nlfit->tolerance);
587 TextSetString(ui->tol_item, buf);
588 SetOptionChoice(ui->nparm_item, nlfit->parnum);
589 for (i = 0; i < MAXPARM; i++) {
590 nonlparm *nlp = &nlfit->parms[i];
591 sprintf(buf, "%g", nlp->value);
592 TextSetString(ui->value_item[i], buf);
593 ToggleButtonSetState(ui->constr_item[i], nlp->constr);
594 sprintf(buf, "%g", nlp->min);
595 TextSetString(ui->lowb_item[i], buf);
596 WidgetSetSensitive(ui->lowb_item[i]->form, nlp->constr);
597 sprintf(buf, "%g", nlp->max);
598 TextSetString(ui->uppb_item[i], buf);
599 WidgetSetSensitive(ui->uppb_item[i]->form, nlp->constr);
600 if (i < nlfit->parnum) {
601 if (!WidgetIsManaged (ui->parm_item[i])) {
602 WidgetManage(ui->parm_item[i]);
604 } else {
605 if (WidgetIsManaged (ui->parm_item[i])) {
606 WidgetUnmanage(ui->parm_item[i]);
613 static void nonl_wf_cb(OptionStructure *opt, int value, void *data)
615 Widget rc = XtParent((Widget) data);
617 if (value == WEIGHT_CUSTOM) {
618 WidgetSetSensitive(rc, True);
619 } else {
620 WidgetSetSensitive(rc, False);
624 static void do_nonl_toggle(OptionStructure *opt, int value, void *data)
626 Widget rc = (Widget) data;
628 if (value == LOAD_FUNCTION) {
629 WidgetSetSensitive(rc, True);
630 } else {
631 WidgetSetSensitive(rc, False);
635 static void do_constr_toggle(Widget tbut, int onoff, void *data)
637 #if 0
638 int value = (int) data;
639 if (onoff) {
640 WidgetSetSensitive(ui->lowb_item[value], True);
641 WidgetSetSensitive(ui->uppb_item[value], True);
642 nlfit.parms[value].constr = TRUE;
643 } else {
644 WidgetSetSensitive(ui->lowb_item[value], False);
645 WidgetSetSensitive(ui->uppb_item[value], False);
646 nlfit.parms[value].constr = FALSE;
648 #endif
652 static void create_openfit_popup(Widget but, void *data)
654 static FSBStructure *fsb = NULL;
656 set_wait_cursor();
658 if (fsb == NULL) {
659 fsb = CreateFSBDialog(app_shell, "Open fit parameter file");
660 AddFSBDialogCB(fsb, do_openfit_proc, NULL);
661 FSBDialogSetPattern(fsb, "*.fit");
662 WidgetManage(fsb->FSB);
665 DialogRaise(fsb->FSB);
667 unset_wait_cursor();
670 static int do_openfit_proc(FSBStructure *fsb, char *filename, void *data)
672 errwin("Not implemented yet");
674 return FALSE;
678 static void create_savefit_popup(Widget but, void *data)
680 static FSBStructure *fsb = NULL;
681 static TextStructure *title_item = NULL;
683 set_wait_cursor();
685 if (fsb == NULL) {
686 Widget fr;
688 fsb = CreateFSBDialog(app_shell, "Save fit parameter file");
689 fr = CreateFrame(fsb->rc, NULL);
690 title_item = CreateText2(fr, "Title: ", 25);
691 AddFSBDialogCB(fsb, do_savefit_proc, title_item);
692 FSBDialogSetPattern(fsb, "*.fit");
693 WidgetManage(fsb->FSB);
696 /* xv_setstr(title_item, ui->nlfit.title); */
698 DialogRaise(fsb->FSB);
700 unset_wait_cursor();
703 static int do_savefit_proc(FSBStructure *fsb, char *filename, void *data)
705 FILE *pp;
706 /* Widget title_item = (Widget) data; */
708 pp = gapp_openw(gapp, filename);
709 if (pp != NULL) {
710 #if 0
711 nlfit.title = copy_string(nlfit.title, xv_getstr(title_item));
712 #endif
713 errwin("Not implemented yet");
714 /* FIXME */;
715 gapp_close(pp);
717 return TRUE;