2 * Grace - GRaphing, Advanced Computation and Exploration of data
4 * Home page: http://plasma-gate.weizmann.ac.il/Grace/
6 * Copyright (c) 1991-1995 Paul J Turner, Portland, OR
7 * Copyright (c) 1996-2003 Grace Development Team
9 * Maintained by Evgeny Stambulchik
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.
31 * non linear curve fitting
41 #include <Xm/ScrolledW.h>
45 #include "core_utils.h"
51 /* nonlprefs.load possible values */
53 #define LOAD_RESIDUALS 1
54 #define LOAD_FUNCTION 2
60 #define WEIGHT_CUSTOM 4
62 /* prefs for non-linear fit */
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 ... */
72 TextStructure
*formula_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
];
80 OptionStructure
*nparm_item
;
81 SpinStructure
*nsteps_item
;
82 OptionStructure
*load_item
;
85 Widget start_item
, stop_item
;
87 RestrictionStructure
*restr_item
;
88 OptionStructure
*weigh_item
;
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
;
127 for (i
= 0; i
< MAXPARM
; i
++) {
129 WidgetManage(ui
->parm_item
[i
]);
131 WidgetUnmanage(ui
->parm_item
[i
]);
136 static void reset_frame_cb(Widget but
, void *data
)
138 Nonl_ui
*ui
= (Nonl_ui
*) data
;
141 memset(&nlfit
, 0, sizeof(NLFit
));
145 update_nonl_frame(ui
, &nlfit
);
148 static void *nonl_build_cb(TransformStructure
*tdialog
)
152 ui
= xmalloc(sizeof(Nonl_ui
));
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',
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
,
222 XmNscrollingPolicy
, XmAUTOMATIC
,
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");
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
);
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");
294 static void nonl_free_cb(void *tddata
)
296 Nonl_pars
*pars
= (Nonl_pars
*) tddata
;
298 xfree(pars
->nlfit
.title
);
299 xfree(pars
->nlfit
.formula
);
306 static void *nonl_get_cb(void *gui
)
308 Nonl_ui
*ui
= (Nonl_ui
*) gui
;
311 pars
= xmalloc(sizeof(Nonl_pars
));
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
++) {
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");
333 nlp
->constr
= GetToggleButtonState(ui
->constr_item
[i
]);
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");
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");
347 if ((nlp
->value
< nlp
->min
) || (nlp
->value
> nlp
->max
)) {
348 errmsg("Initial values must be within bounds");
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");
364 if (xv_evalexpr(ui
->stop_item
, &pars
->prefs
.stop
) != RETURN_SUCCESS
) {
365 errmsg("Invalid input in stop field");
369 if (xv_evalexpri(ui
->npts_item
, &pars
->prefs
.npoints
) != RETURN_SUCCESS
) {
370 errmsg("Invalid input in npoints field");
374 if (pars
->prefs
.npoints
<= 1) {
375 errmsg("Number of points must be > 1");
382 for (i
= 0; i
< nlfit
->parnum
; i
++) {
383 nonlparm
*nlp
= &nlfit
->parms
[i
];
386 var
= define_parser_scalar(nlp
->name
);
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
)
403 Nonl_pars
*pars
= (Nonl_pars
*) tddata
;
407 double *ytmp
, *warray
;
410 /* apply weigh function */
411 nlen
= set_get_length(psrc
);
412 switch (pars
->weight_method
) {
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
];
431 warray
[i
] = 1/(ytmp
[i
]*ytmp
[i
]);
436 ytmp
= set_get_col(psrc
, DATA_Y1
);
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
]);
456 if (set_parser_setno(psrc
) != RETURN_SUCCESS
) {
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
;
466 errmsg("The array of weights has different length");
468 return RETURN_FAILURE
;
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");
482 return RETURN_FAILURE
;
485 /* The fit itself! */
486 res
= do_nonlfit(psrc
, &pars
->nlfit
, warray
, rarray
, pars
->nsteps
);
488 /* Free temp arrays */
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
) {
505 double delx
, *xfit
, *y
, *yfit
;
507 switch (pars
->prefs
.load
) {
510 npts
= set_get_length(psrc
);
511 set_set_length(pdest
, npts
);
512 copycol2(psrc
, pdest
, DATA_X
);
515 npts
= pars
->prefs
.npoints
;
517 set_set_length(pdest
, npts
);
519 delx
= (pars
->prefs
.stop
- pars
->prefs
.start
)/(npts
- 1);
521 for (i
= 0; i
< npts
; i
++) {
522 xfit
[i
] = pars
->prefs
.start
+ i
* delx
;
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 */
534 for (i
= 0; i
< npts
; i
++) {
540 return RETURN_SUCCESS
;
543 void create_nonl_frame(Widget but
, void *data
)
545 static TransformStructure
*tdialog
= NULL
;
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
)
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
]);
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
);
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
);
620 WidgetSetSensitive(rc
, False
);
624 static void do_constr_toggle(Widget tbut
, int onoff
, void *data
)
627 int value
= (int) data
;
629 WidgetSetSensitive(ui
->lowb_item
[value
], True
);
630 WidgetSetSensitive(ui
->uppb_item
[value
], True
);
631 nlfit
.parms
[value
].constr
= TRUE
;
633 WidgetSetSensitive(ui
->lowb_item
[value
], False
);
634 WidgetSetSensitive(ui
->uppb_item
[value
], False
);
635 nlfit
.parms
[value
].constr
= FALSE
;
641 static void create_openfit_popup(Widget but
, void *data
)
643 static FSBStructure
*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
);
659 static int do_openfit_proc(FSBStructure
*fsb
, char *filename
, void *data
)
661 errwin("Not implemented yet");
667 static void create_savefit_popup(Widget but
, void *data
)
669 static FSBStructure
*fsb
= NULL
;
670 static Widget title_item
= NULL
;
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
);
692 static int do_savefit_proc(FSBStructure
*fsb
, char *filename
, void *data
)
695 /* Widget title_item = (Widget) data; */
697 pp
= gapp_openw(gapp
, filename
);
700 nlfit
.title
= copy_string(nlfit
.title
, xv_getstr(title_item
));
702 errwin("Not implemented yet");