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 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
;
84 TextStructure
*npts_item
;
85 TextStructure
*start_item
, *stop_item
;
87 RestrictionStructure
*restr_item
;
88 OptionStructure
*weigh_item
;
89 TextStructure
*wfunc_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 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',
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
,
224 XmNscrollingPolicy
, XmAUTOMATIC
,
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");
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
);
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");
296 static void nonl_free_cb(void *tddata
)
298 Nonl_pars
*pars
= (Nonl_pars
*) tddata
;
300 xfree(pars
->nlfit
.title
);
301 xfree(pars
->nlfit
.formula
);
308 static void *nonl_get_cb(void *gui
)
310 Nonl_ui
*ui
= (Nonl_ui
*) gui
;
313 pars
= xmalloc(sizeof(Nonl_pars
));
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
);
324 nlfit
->parnum
= GetOptionChoice(ui
->nparm_item
);
326 pars
->nsteps
= (int) SpinChoiceGetValue(ui
->nsteps_item
);
328 for (i
= 0; i
< nlfit
->parnum
; i
++) {
330 nonlparm
*nlp
= &nlfit
->parms
[i
];
331 s
= TextGetString(ui
->value_item
[i
]);
334 if (sscanf(buf
, "%lf", &nlp
->value
) != 1) {
335 errmsg("Invalid input in parameter field");
340 nlp
->constr
= ToggleButtonGetState(ui
->constr_item
[i
]);
342 s
= TextGetString(ui
->lowb_item
[i
]);
345 if (sscanf(buf
, "%lf", &nlp
->min
) != 1) {
346 errmsg("Invalid input in low-bound field");
350 s
= TextGetString(ui
->uppb_item
[i
]);
353 if (sscanf(buf
, "%lf", &nlp
->max
) != 1) {
354 errmsg("Invalid input in upper-bound field");
358 if ((nlp
->value
< nlp
->min
) || (nlp
->value
> nlp
->max
)) {
359 errmsg("Initial values must be within bounds");
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");
375 if (xv_evalexpr(ui
->stop_item
, &pars
->prefs
.stop
) != RETURN_SUCCESS
) {
376 errmsg("Invalid input in stop field");
380 if (xv_evalexpri(ui
->npts_item
, &pars
->prefs
.npoints
) != RETURN_SUCCESS
) {
381 errmsg("Invalid input in npoints field");
385 if (pars
->prefs
.npoints
<= 1) {
386 errmsg("Number of points must be > 1");
393 for (i
= 0; i
< nlfit
->parnum
; i
++) {
394 nonlparm
*nlp
= &nlfit
->parms
[i
];
397 var
= define_parser_scalar(nlp
->name
);
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
)
414 Nonl_pars
*pars
= (Nonl_pars
*) tddata
;
418 double *ytmp
, *warray
;
421 /* apply weigh function */
422 nlen
= set_get_length(psrc
);
423 switch (pars
->weight_method
) {
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
];
442 warray
[i
] = 1/(ytmp
[i
]*ytmp
[i
]);
447 ytmp
= set_get_col(psrc
, DATA_Y1
);
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
]);
467 if (set_parser_setno(psrc
) != RETURN_SUCCESS
) {
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
;
477 errmsg("The array of weights has different length");
479 return RETURN_FAILURE
;
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");
493 return RETURN_FAILURE
;
496 /* The fit itself! */
497 res
= do_nonlfit(psrc
, &pars
->nlfit
, warray
, rarray
, pars
->nsteps
);
499 /* Free temp arrays */
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
) {
516 double delx
, *xfit
, *y
, *yfit
;
518 switch (pars
->prefs
.load
) {
521 npts
= set_get_length(psrc
);
522 set_set_length(pdest
, npts
);
523 copycol2(psrc
, pdest
, DATA_X
);
526 npts
= pars
->prefs
.npoints
;
528 set_set_length(pdest
, npts
);
530 delx
= (pars
->prefs
.stop
- pars
->prefs
.start
)/(npts
- 1);
532 for (i
= 0; i
< npts
; i
++) {
533 xfit
[i
] = pars
->prefs
.start
+ i
* delx
;
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 */
545 for (i
= 0; i
< npts
; i
++) {
551 return RETURN_SUCCESS
;
554 void create_nonl_frame(Widget but
, void *data
)
556 static TransformStructure
*tdialog
= NULL
;
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
)
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
]);
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
);
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
);
631 WidgetSetSensitive(rc
, False
);
635 static void do_constr_toggle(Widget tbut
, int onoff
, void *data
)
638 int value
= (int) data
;
640 WidgetSetSensitive(ui
->lowb_item
[value
], True
);
641 WidgetSetSensitive(ui
->uppb_item
[value
], True
);
642 nlfit
.parms
[value
].constr
= TRUE
;
644 WidgetSetSensitive(ui
->lowb_item
[value
], False
);
645 WidgetSetSensitive(ui
->uppb_item
[value
], False
);
646 nlfit
.parms
[value
].constr
= FALSE
;
652 static void create_openfit_popup(Widget but
, void *data
)
654 static FSBStructure
*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
);
670 static int do_openfit_proc(FSBStructure
*fsb
, char *filename
, void *data
)
672 errwin("Not implemented yet");
678 static void create_savefit_popup(Widget but
, void *data
)
680 static FSBStructure
*fsb
= NULL
;
681 static TextStructure
*title_item
= NULL
;
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
);
703 static int do_savefit_proc(FSBStructure
*fsb
, char *filename
, void *data
)
706 /* Widget title_item = (Widget) data; */
708 pp
= gapp_openw(gapp
, filename
);
711 nlfit
.title
= copy_string(nlfit
.title
, xv_getstr(title_item
));
713 errwin("Not implemented yet");