FazBrowse GitHub Viewer
|
Trending
|
URL:
|
Home
Tools:
[Download Repo ZIP]
[View Raw Code]
[Original HTTPS Page]
gnuplot/src/fit.c at master · gnuplot/gnuplot · GitHub
gnuplot
gnuplot
Repository navigation
Code
Pull requests
2
(2)
Actions
Security and quality
Insights
Expand file tree
Breadcrumbs
gnuplot
/
src
/
fit.c
Copy path
More file actions
More file actions
Latest commit
History
History
History
2449 lines (2147 loc) · 75.1 KB
Breadcrumbs
gnuplot
/
src
/
fit.c
Copy path
File metadata and controls
2449 lines (2147 loc) · 75.1 KB
Raw
Copy raw file
Download raw file
Open symbols panel
Edit and raw actions
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/* NOTICE: Change of Copyright Status
*
* The author of this module, Carsten Grammes, has expressed in
* personal email that he has no more interest in this code, and
* doesn't claim any copyright. He has agreed to put this module
* into the public domain.
*
* Lars Hecking 15-02-1999
*/
/*
* Nonlinear least squares fit according to the
* Marquardt-Levenberg-algorithm
*
* added as Patch to Gnuplot (v3.2 and higher)
* by Carsten Grammes
*
* Michele Marziani (marziani@ferrara.infn.it), 930726: Recoding of the
* Unix-like raw console I/O routines.
*
* drd: start unitialised variables at 1 rather than NEARLY_ZERO
* (fit is more likely to converge if started from 1 than 1e-30 ?)
*
* HBB (broeker@physik.rwth-aachen.de) : fit didn't calculate the errors
* in the 'physically correct' (:-) way, if a third data column containing
* the errors (or 'uncertainties') of the input data was given. I think
* I've fixed that, but I'm not sure I really understood the M-L-algo well
* enough to get it right. I deduced my change from the final steps of the
* equivalent algorithm for the linear case, which is much easier to
* understand. (I also made some minor, mostly cosmetic changes)
*
* HBB (again): added error checking for negative covar[i][i] values and
* for too many parameters being specified.
*
* drd: allow 3d fitting. Data value is now called fit_z internally,
* ie a 2d fit is z vs x, and a 3d fit is z vs x and y.
*
* HBB, 971023: lifted fixed limit on number of datapoints, and number
* of parameters.
*
* HBB/H.Harders, 20020927: log file name now changeable from inside
* gnuplot, not only by setting an environment variable.
*
* Jim Van Zandt, 090201: allow fitting functions with up to five
* independent variables.
*
* Carl Michal, 120311: optionally prescale all the parameters that
* the L-M routine sees by their initial values, so that parameters
* that differ by many orders of magnitude do not cause problems.
* With decent initial guesses, fits often take fewer iterations. If
* any variables were 0, then don't do it for those variables, since
* it may do more harm than good.
*
* Thomas Mattison, 130421: brief one-line reports, based on patchset #230.
* Bastian Maerkisch, 130421: different output verbosity levels
*
* Bastian Maerkisch, 130427: remember parameters etc. of last fit and use
* this data in a subsequent update command if the parameter file does not
* exist yet.
*
* Thomas Mattison, 130508: New convergence criterion which is absolute
* reduction in chisquare for an iteration of less than epsilon*chisquare
* plus epsilon_abs (new setting). The default convergence criterion is
* always relative no matter what the chisquare is, but users now have the
* flexibility of adding an absolute convergence criterion through
* `set fit limit_abs`. Patchset #230.
*
* Ethan A Merritt, June 2013: Remove limit of 5 independent parameters.
* The limit is now the smaller of MAXDATACOLS-2 and MAX_NUM_VAR.
* Dissociate parameters other than x/y from "normal" plot axes.
* To refine more than 2 parameters, name them with `set dummy`.
* x and y still refer to axis_array[] in order to allow time/date formats.
*
* Bastian Maerkisch, Feb 2014: New syntax to specify errors. The new
* parameter 'errors' accepts a comma separated list of (dummy) variables
* to specify which (in-)dependent variable has associated errors. 'z'
* always denotes the indep. variable. 'unitweights' tells fit to use equal
* (1) weights for the fit. The new syntax removes the ambiguity between
* x:y:z:(1) and x:z:s. The old syntax is still accepted but deprecated.
*
* Alexander Taeschner, Feb 2014: Optionally take errors of independent
* variables into account.
*
* Bastian Maerkisch, Feb 2014: Split regress() into several functions
* in order to facilitate the inclusion of alternative fitting codes.
*
* Karl Ratzsch, May 2014: Add a result variable reporting the number of
* iterations
*
* Ethan Merritt, Mar 2021: Wrap the entire fit command in an exception
* handler so that a user script can recover from fit errors.
*/
#include
"fit.h"
#include
"alloc.h"
#include
"command.h"
#include
"datablock.h"
#include
"datafile.h"
#include
"eval.h"
#include
"gplocale.h"
#include
"gp_time.h"
#include
"matrix.h"
#include
"misc.h"
#include
"plot.h"
#include
"setshow.h"
#include
"scanner.h"
/* For legal_identifier() */
#include
"specfun.h"
#include
"util.h"
#include
<signal.h>
/* Just temporary */
#if
defined(
VA_START
)
&&
defined(
STDC_HEADERS
)
static
void
Dblfn
(
const
char
*
fmt
, ...);
#else
static
void
Dblfn
();
#endif
#define
Dblf
Dblfn
#define
Dblf2
Dblfn
#define
Dblf3
Dblfn
#define
Dblf5
Dblfn
#define
Dblf6
Dblfn
#if
defined(
MSDOS
)
/* non-blocking IO stuff */
# include
<io.h>
# include
<conio.h>
# include
<dos.h>
#endif
#ifdef
_WIN32
# include
<fcntl.h>
# include
"win/winmain.h"
#endif
/* constants */
#ifdef
INFINITY
# undef
INFINITY
#endif
#define
INFINITY
1e30
#define
NEARLY_ZERO
1e-30
/* create new variables with this value (was NEARLY_ZERO) */
#define
INITIAL_VALUE
1.0
/* Relative change for derivatives */
#define
DELTA
0.001
#define
MAX_DATA
2048
#define
MAX_PARAMS
32
#define
MAX_LAMBDA
1e20
#define
MIN_LAMBDA
1e-20
#define
LAMBDA_UP_FACTOR
10
#define
LAMBDA_DOWN_FACTOR
10
#if
defined(
MSDOS
)
||
defined(
OS2
)
# define
PLUSMINUS
"\xF1"
/* plusminus sign */
#else
# define
PLUSMINUS
"+/-"
#endif
#define
LASTFITCMDLENGTH
511
/* compatible with gnuplot philosophy */
#define
STANDARD
stderr
/* Suffix of a backup file */
#define
BACKUP_SUFFIX
".old"
#define
SQR
(
x
) ((x) * (x))
/* type definitions */
enum
marq_res
{
OK
,
ML_ERROR
,
BETTER
,
WORSE
};
typedef
enum
marq_res
marq_res_t
;
/* externally visible variables: */
/* pointer to longjmp recovery point of "fit" command */
JMP_BUF
*
fit_env
=
NULL
;
/* fit control */
char
*
fitlogfile
=
NULL
;
TBOOLEAN
fit_suppress_log
=
FALSE;
TBOOLEAN
fit_errorvariables
=
TRUE;
TBOOLEAN
fit_covarvariables
=
FALSE;
verbosity_level
fit_verbosity
=
BRIEF
;
TBOOLEAN
fit_errorscaling
=
TRUE;
TBOOLEAN
fit_prescale
=
TRUE;
char
*
fit_script
=
NULL
;
int
fit_wrap
=
0
;
TBOOLEAN
fit_v4compatible
=
FALSE;
char
*
last_fit_command
=
NULL
;
/* names of user control variables */
const
char
*
FITLIMIT
=
"FIT_LIMIT"
;
const
char
*
FITSTARTLAMBDA
=
"FIT_START_LAMBDA"
;
const
char
*
FITLAMBDAFACTOR
=
"FIT_LAMBDA_FACTOR"
;
const
char
*
FITMAXITER
=
"FIT_MAXITER"
;
/* private variables: */
static
double
epsilon
=
DEF_FIT_LIMIT
;
/* relative convergence limit */
double
epsilon_abs
=
0.0
;
/* default to zero non-relative limit */
int
maxiter
=
0
;
static
double
startup_lambda
=
0
;
static
double
lambda_down_factor
=
LAMBDA_DOWN_FACTOR
;
static
double
lambda_up_factor
=
LAMBDA_UP_FACTOR
;
static
const
char
fitlogfile_default
[]
=
"fit.log"
;
static
const
char
GNUFITLOG
[]
=
"FIT_LOG"
;
static
FILE
*
log_f
=
NULL
;
static
FILE
*
via_f
=
NULL
;
static
TBOOLEAN
fit_show_lambda
=
TRUE;
static
const
char
*
GP_FIXED
=
"# FIXED"
;
static
const
char
*
FITSCRIPT
=
"FIT_SCRIPT"
;
static
const
char
*
DEFAULT_CMD
=
"replot"
;
/* if no fitscript spec. */
static
int
num_data
;
static
int
num_params
;
static
int
num_indep
;
/* # independent variables in fit function */
static
int
num_errors
;
/* # error columns */
static
TBOOLEAN
err_cols
[
MAX_NUM_VAR
+
1
];
/* TRUE if variable has an associated error */
static
int
columns
;
/* # values read from data file for each point */
static
double
*
fit_x
=
0
;
/* all independent variable values,
e.g. value of the ith variable from
the jth data point is in
fit_x[j*num_indep+i] */
static
double
*
fit_z
=
0
;
/* dependent data values */
static
double
*
err_data
=
0
;
/* standard deviations of indep. and dependent data */
static
double
*
a
=
0
;
/* array of fitting parameters */
static
double
*
*
regress_C
=
0
;
/* global copy of C matrix in regress */
static
void
(
*
regress_cleanup
)(
void
)
=
NULL
;
/* memory cleanup function callback */
static
TBOOLEAN
user_stop
=
FALSE;
static
double
*
scale_params
=
0
;
/* scaling values for parameters */
static
struct
udft_entry
func
;
static
fixstr
*
par_name
;
static
t_value
*
*
par_udv
;
/* array of pointers to the "via" variables */
static
fixstr
*
last_par_name
=
NULL
;
static
int
last_num_params
=
0
;
static
char
*
last_dummy_var
[
MAX_NUM_VAR
];
/* Mar 2014 - the single hottest call path in fit was looking up the
* dummy parameters by name (4 billion times in fit.dem).
* A total waste, since they don't change. Look up once and store here.
*/
static
udvt_entry
*
fit_dummy_udvs
[
MAX_NUM_VAR
];
/*****************************************************************
internal Prototypes
*****************************************************************/
#if
!defined(
_WIN32
)
||
defined(
WGP_CONSOLE
)
static
RETSIGTYPE
ctrlc_handle
(
int
an_int
);
#endif
static
void
ctrlc_setup
(
void
);
static
void
fit_main
(
void
);
static
marq_res_t
marquardt
(
double
a
[],
double
*
*
alpha
,
double
*
chisq
,
double
*
lambda
);
static
void
analyze
(
double
a
[],
double
*
*
alpha
,
double
beta
[],
double
*
chisq
,
double
*
*
deriv
);
static
void
calculate
(
double
*
zfunc
,
double
*
*
dzda
,
double
a
[]);
static
void
calc_derivatives
(
const
double
*
par
,
double
*
data
,
double
*
*
deriv
);
static
TBOOLEAN
fit_interrupt
(
void
);
static
TBOOLEAN
regress
(
double
a
[]);
static
void
regress_init
(
void
);
static
void
regress_finalize
(
int
iter
,
double
chisq
,
double
last_chisq
,
double
lambda
,
double
*
*
covar
);
static
void
fit_show
(
int
i
,
double
chisq
,
double
last_chisq
,
double
*
a
,
double
lambda
,
FILE
*
device
);
static
void
fit_show_brief
(
int
iter
,
double
chisq
,
double
last_chisq
,
double
*
parms
,
double
lambda
,
FILE
*
device
);
static
void
show_results
(
double
chisq
,
double
last_chisq
,
double
*
a
,
double
*
dpar
,
double
*
*
corel
);
static
void
log_axis_restriction
(
FILE
*
log_f
,
int
param
,
double
min
,
double
max
,
int
autoscale
,
char
*
name
);
static
void
print_function_definitions
(
struct
at_type
*
at
,
FILE
*
device
);
static
TBOOLEAN
is_empty
(
char
*
s
);
static
intgr_t
getivar
(
const
char
*
varname
);
static
double
getdvar
(
const
char
*
varname
);
static
double
createdvar
(
char
*
varname
,
double
value
);
static
void
setvar
(
char
*
varname
,
double
value
);
static
void
setvarerr
(
char
*
varname
,
double
value
);
static
void
setvarcovar
(
char
*
varname1
,
char
*
varname2
,
double
value
);
static
char
*
get_next_word
(
char
*
*
s
,
char
*
subst
);
/*****************************************************************
Interface to the gnuplot "fit" command
*****************************************************************/
void
fit_command
()
{
static
JMP_BUF
fit_jumppoint
;
if
(
evaluate_inside_functionblock
&&
inside_plot_command
)
int_error
(
NO_CARET
,
"fit command not possible in this context"
);
inside_plot_command
=
TRUE;
/* Set up an exception handler for errors that occur during "fit".
* Normally these would return to the top level command parser via
* a longjmp from int_error() and bail_to_command_line().
* We introduce a separate jump point here so that a call to "fit"
* always returns to the call point regardless of success or error.
* The caller must then check for success.
*/
fit_env
=
&
fit_jumppoint
;
if
(
SETJMP
(
*
fit_env
,
1
)) {
fit_env
=
NULL
;
fprintf
(
stderr
,
"*** FIT ERROR ***\n"
);
free
(
last_fit_command
);
last_fit_command
=
NULL
;
while
(!
END_OF_COMMAND
)
c_token
++
;
Ginteger
(
&
(
add_udv_by_name
(
"FIT_ERROR"
)
->
udv_value
),
1
);
inside_plot_command
=
FALSE;
return
;
}
fit_main
();
fit_env
=
NULL
;
Ginteger
(
&
(
add_udv_by_name
(
"FIT_ERROR"
)
->
udv_value
),
0
);
inside_plot_command
=
FALSE;
}
/*****************************************************************
This is called when a SIGINT occurs during fit
*****************************************************************/
#if
!defined(
_WIN32
)
||
defined(
WGP_CONSOLE
)
static
RETSIGTYPE
ctrlc_handle
(
int
an_int
)
{
(
void
)
an_int
;
/* avoid -Wunused warning */
/* reinstall signal handler (necessary on SysV) */
(
void
)
signal
(
SIGINT
, (
sigfunc
)
ctrlc_handle
);
ctrlc_flag
=
TRUE;
}
#endif
/*****************************************************************
setup the ctrl_c signal handler
*****************************************************************/
static
void
ctrlc_setup
()
{
/*
* MSDOS defines signal(SIGINT) but doesn't handle it through
* real interrupts. So there remain cases in which a ctrl-c may
* be uncaught by signal. We must use kbhit() instead that really
* serves the keyboard interrupt (or write an own interrupt func
* which also generates #ifdefs)
*
* I hope that other OSes do it better, if not... add #ifdefs :-(
*/
#if
(defined(
__EMX__
)
||
!defined(
MSDOS
))
&&
(!defined(
_WIN32
)
||
defined(
WGP_CONSOLE
))
(
void
)
signal
(
SIGINT
, (
sigfunc
)
ctrlc_handle
);
#endif
}
/*****************************************************************
getch that handles also function keys etc.
*****************************************************************/
#if
defined(
MSDOS
)
int
getchx
(
void
);
int
getchx
()
{
int
c
=
getch
();
if
(!
c
||
c
==
0xE0
) {
c
<<=
8
;
c
|=
getch
();
}
return
c
;
}
#endif
/*****************************************************************
Clean up data structures specfic to fatal errors during fit.
After this we invoke the generic int_error(),
which in turn returrns via longjmp to the start of this "fit".
*****************************************************************/
void
error_ex
(
int
t_num
,
const
char
*
str
, ...)
{
char
buf
[
128
];
va_list
args
;
va_start
(
args
,
str
);
vsnprintf
(
buf
,
sizeof
(
buf
),
str
,
args
);
va_end
(
args
);
/* cleanup - free memory */
if
(
log_f
) {
fprintf
(
log_f
,
"BREAK: %s"
,
buf
);
fclose
(
log_f
);
log_f
=
NULL
;
}
if
(
via_f
) {
fclose
(
via_f
);
via_f
=
NULL
;
}
free
(
fit_x
);
free
(
fit_z
);
free
(
err_data
);
free
(
a
);
a
=
fit_x
=
fit_z
=
err_data
=
NULL
;
if
(
func
.
at
) {
free_at
(
func
.
at
);
/* release perm. action table */
func
.
at
=
(
struct
at_type
*
)
NULL
;
}
if
(
regress_cleanup
!=
NULL
)
(
*
regress_cleanup
)();
/* the datafile may still be open */
df_close
();
/* restore original SIGINT function */
interrupt_setup
();
/* Usually int_error() returns to the top level command parser
* via longjmp at a point equivalent to program entry.
* However in this case we introduced a nested handler at the
* start of command processing in fit_command() so that control
* returns after the "fit" command regardless of success or
* failure. It is then up to the caller to continue or not
* after a failed "fit".
*/
int_error
(
t_num
,
buf
);
}
/*****************************************************************
Marquardt's nonlinear least squares fit
*****************************************************************/
static
marq_res_t
marquardt
(
double
a
[],
double
*
*
C
,
double
*
chisq
,
double
*
lambda
)
{
int
i
,
j
;
static
double
*
da
=
0
,
/* delta-step of the parameter */
*
temp_a
=
0
,
/* temptative new params set */
*
d
=
0
,
*
tmp_d
=
0
,
*
*
tmp_C
=
0
,
*
residues
=
0
,
*
*
deriv
=
0
;
double
tmp_chisq
;
/* Initialization when lambda == -1 */
if
(
*
lambda
==
-1
) {
/* Get first chi-square check */
temp_a
=
vec
(
num_params
);
d
=
vec
(
num_data
+
num_params
);
tmp_d
=
vec
(
num_data
+
num_params
);
da
=
vec
(
num_params
);
residues
=
vec
(
num_data
+
num_params
);
tmp_C
=
matr
(
num_data
+
num_params
,
num_params
);
deriv
=
NULL
;
if
(
num_errors
>
1
)
deriv
=
matr
(
num_errors
-
1
,
num_data
);
analyze
(
a
,
C
,
d
,
chisq
,
deriv
);
/* Calculate a useful startup value for lambda, as given by Schwarz */
if
(
startup_lambda
!=
0
)
*
lambda
=
startup_lambda
;
else
{
*
lambda
=
0
;
for
(
i
=
0
;
i
<
num_data
;
i
++
)
for
(
j
=
0
;
j
<
num_params
;
j
++
)
*
lambda
+=
C
[
i
][
j
]
*
C
[
i
][
j
];
*
lambda
=
sqrt
(
*
lambda
/
num_data
/
num_params
);
}
/* Fill in the lower square part of C (the diagonal is filled in on
each iteration, see below) */
for
(
i
=
0
;
i
<
num_params
;
i
++
)
for
(
j
=
0
;
j
<
i
;
j
++
)
C
[
num_data
+
i
][
j
]
=
0
,
C
[
num_data
+
j
][
i
]
=
0
;
return
OK
;
}
/* once converged, free allocated vars */
if
(
*
lambda
==
-2
) {
free
(
d
);
free
(
tmp_d
);
free
(
da
);
free
(
temp_a
);
free
(
residues
);
free_matr
(
tmp_C
);
free_matr
(
deriv
);
/* may be called more than once */
d
=
tmp_d
=
da
=
temp_a
=
residues
=
(
double
*
)
NULL
;
tmp_C
=
deriv
=
(
double
*
*
)
NULL
;
return
OK
;
}
/* Givens calculates in-place, so make working copies of C and d */
for
(
j
=
0
;
j
<
num_data
+
num_params
;
j
++
)
memcpy
(
tmp_C
[
j
],
C
[
j
],
num_params
*
sizeof
(
double
));
memcpy
(
tmp_d
,
d
,
num_data
*
sizeof
(
double
));
/* fill in additional parts of tmp_C, tmp_d */
for
(
i
=
0
;
i
<
num_params
;
i
++
) {
/* fill in low diag. of tmp_C ... */
tmp_C
[
num_data
+
i
][
i
]
=
*
lambda
;
/* ... and low part of tmp_d */
tmp_d
[
num_data
+
i
]
=
0
;
}
Givens
(
tmp_C
,
tmp_d
,
da
,
num_params
+
num_data
,
num_params
);
/* check if trial did ameliorate sum of squares */
for
(
j
=
0
;
j
<
num_params
;
j
++
)
temp_a
[
j
]
=
a
[
j
]
+
da
[
j
];
analyze
(
temp_a
,
tmp_C
,
tmp_d
,
&
tmp_chisq
,
deriv
);
/* tsm patchset 230: Changed < to <= in next line */
/* so finding exact minimum stops iteration instead of just increasing lambda. */
/* Disadvantage is that if lambda is large enough so that chisq doesn't change */
/* is taken as success. */
if
(
tmp_chisq
<=
*
chisq
) {
/* Success, accept new solution */
if
(
*
lambda
>
MIN_LAMBDA
) {
if
(
fit_verbosity
==
VERBOSE
)
putc
(
'/'
,
stderr
);
*
lambda
/=
lambda_down_factor
;
}
/* update chisq, C, d, a */
*
chisq
=
tmp_chisq
;
for
(
j
=
0
;
j
<
num_data
;
j
++
) {
memcpy
(
C
[
j
],
tmp_C
[
j
],
num_params
*
sizeof
(
double
));
d
[
j
]
=
tmp_d
[
j
];
}
for
(
j
=
0
;
j
<
num_params
;
j
++
)
a
[
j
]
=
temp_a
[
j
];
return
BETTER
;
}
else
{
/* failure, increase lambda and return */
*
lambda
*=
lambda_up_factor
;
if
(
fit_verbosity
==
VERBOSE
)
(
void
)
putc
(
'*'
,
stderr
);
else
if
(
fit_verbosity
==
BRIEF
)
/* one-line report even if chisq increases */
fit_show_brief
(
-1
,
tmp_chisq
,
*
chisq
,
temp_a
,
*
lambda
,
STANDARD
);
return
WORSE
;
}
}
/*****************************************************************
compute the (effective) error
*****************************************************************/
static
double
effective_error
(
double
*
*
deriv
,
int
i
)
{
double
tot_err
;
int
j
,
k
;
if
(
num_errors
<=
1
)
/* z-errors or equal weights */
tot_err
=
err_data
[
i
];
else
{
/* "Effective variance" according to
* Jay Orear, Am. J. Phys., Vol. 50, No. 10, October 1982
*/
tot_err
=
SQR
(
err_data
[
i
*
num_errors
+
(
num_errors
-
1
)]);
for
(
j
=
0
,
k
=
0
;
j
<
num_indep
;
j
++
) {
if
(
err_cols
[
j
]) {
tot_err
+=
SQR
(
deriv
[
k
][
i
]
*
err_data
[
i
*
num_errors
+
k
]);
k
++
;
}
}
tot_err
=
sqrt
(
tot_err
);
}
return
tot_err
;
}
/*****************************************************************
compute chi-square and numeric derivations
*****************************************************************/
/* Used by marquardt to evaluate the linearized fitting matrix C and
* vector d. Fills in only the top part of C and d. I don't use a
* temporary array zfunc[] any more. Just use d[] instead. */
static
void
analyze
(
double
a
[],
double
*
*
C
,
double
d
[],
double
*
chisq
,
double
*
*
deriv
)
{
int
i
,
j
;
calculate
(
d
,
C
,
a
);
/* derivatives in indep. variables are required for
effective variance method */
if
(
num_errors
>
1
)
calc_derivatives
(
a
,
d
,
deriv
);
for
(
i
=
0
;
i
<
num_data
;
i
++
) {
double
err
=
effective_error
(
deriv
,
i
);
/* note: order reversed, as used by Schwarz */
d
[
i
]
=
(
d
[
i
]
-
fit_z
[
i
]) /
err
;
for
(
j
=
0
;
j
<
num_params
;
j
++
)
C
[
i
][
j
] /=
err
;
}
*
chisq
=
sumsq_vec
(
num_data
,
d
);
}
/*****************************************************************
compute function values and partial derivatives of chi-square
*****************************************************************/
/* To use the more exact, but slower two-side formula, activate the
following line: */
/*#define TWO_SIDE_DIFFERENTIATION */
static
void
calculate
(
double
*
zfunc
,
double
*
*
dzda
,
double
a
[])
{
int
k
,
p
;
double
tmp_a
;
double
*
tmp_high
,
*
tmp_pars
;
#ifdef
TWO_SIDE_DIFFERENTIATION
double
*
tmp_low
;
#endif
tmp_high
=
vec
(
num_data
);
/* numeric derivations */
#ifdef
TWO_SIDE_DIFFERENTIATION
tmp_low
=
vec
(
num_data
);
#endif
tmp_pars
=
vec
(
num_params
);
/* first function values */
call_gnuplot
(
a
,
zfunc
);
/* then derivatives in parameters */
for
(
p
=
0
;
p
<
num_params
;
p
++
)
tmp_pars
[
p
]
=
a
[
p
];
for
(
p
=
0
;
p
<
num_params
;
p
++
) {
tmp_a
=
fabs
(
a
[
p
])
<
NEARLY_ZERO
?
NEARLY_ZERO
:
a
[
p
];
tmp_pars
[
p
]
=
tmp_a
*
(
1
+
DELTA
);
call_gnuplot
(
tmp_pars
,
tmp_high
);
#ifdef
TWO_SIDE_DIFFERENTIATION
tmp_pars
[
p
]
=
tmp_a
*
(
1
-
DELTA
);
call_gnuplot
(
tmp_pars
,
tmp_low
);
#endif
for
(
k
=
0
;
k
<
num_data
;
k
++
)
#ifdef
TWO_SIDE_DIFFERENTIATION
dzda
[
k
][
p
]
=
(
tmp_high
[
k
]
-
tmp_low
[
k
]) / (
2
*
tmp_a
*
DELTA
);
#else
dzda
[
k
][
p
]
=
(
tmp_high
[
k
]
-
zfunc
[
k
]) / (
tmp_a
*
DELTA
);
#endif
tmp_pars
[
p
]
=
a
[
p
];
}
#ifdef
TWO_SIDE_DIFFERENTIATION
free
(
tmp_low
);
#endif
free
(
tmp_high
);
free
(
tmp_pars
);
}
/*****************************************************************
call internal gnuplot functions
*****************************************************************/
void
call_gnuplot
(
const
double
*
par
,
double
*
data
)
{
int
i
,
j
;
struct
value
v
;
/* set parameters first */
for
(
i
=
0
;
i
<
num_params
;
i
++
)
Gcomplex
(
par_udv
[
i
],
par
[
i
]
*
scale_params
[
i
],
0.0
);
for
(
i
=
0
;
i
<
num_data
;
i
++
) {
/* calculate fit-function value */
/* initialize extra dummy variables from the corresponding
actual variables, if any. */
for
(
j
=
0
;
j
<
MAX_NUM_VAR
;
j
++
) {
double
dummy_value
;
struct
udvt_entry
*
udv
=
fit_dummy_udvs
[
j
];
if
(!
udv
)
int_error
(
NO_CARET
,
"Internal error: lost a dummy parameter!"
);
if
(
udv
->
udv_value
.
type
==
CMPLX
||
udv
->
udv_value
.
type
==
INTGR
)
dummy_value
=
real
(
&
(
udv
->
udv_value
));
else
dummy_value
=
0.0
;
Gcomplex
(
&
func
.
dummy_values
[
j
],
dummy_value
,
0.0
);
}
/* set actual dummy variables from file data */
for
(
j
=
0
;
j
<
num_indep
;
j
++
)
Gcomplex
(
&
func
.
dummy_values
[
j
],
fit_x
[
i
*
num_indep
+
j
],
0.0
);
evaluate_at
(
func
.
at
,
&
v
);
if
(
undefined
||
isnan
(
real
(
&
v
))) {
/* Print useful info on undefined-function error. */
Dblf
(
"\nCurrent data point\n"
);
Dblf
(
"=========================\n"
);
Dblf3
(
"%-15s = %i out of %i\n"
,
"#"
,
i
+
1
,
num_data
);
for
(
j
=
0
;
j
<
num_indep
;
j
++
)
Dblf3
(
"%-15.15s = %-15g\n"
,
c_dummy_var
[
j
],
par
[
j
]
*
scale_params
[
j
]);
Dblf3
(
"%-15.15s = %-15g\n"
,
"z"
,
fit_z
[
i
]);
Dblf
(
"\nCurrent set of parameters\n"
);
Dblf
(
"=========================\n"
);
for
(
j
=
0
;
j
<
num_params
;
j
++
)
Dblf3
(
"%-15.15s = %-15g\n"
,
par_name
[
j
],
par
[
j
]
*
scale_params
[
j
]);
Dblf
(
"\n"
);
if
(
undefined
) {
Eex
(
"Undefined value during function evaluation"
);
}
else
{
Eex
(
"Function evaluation yields NaN (\"not a number\")"
);
}
}
data
[
i
]
=
real
(
&
v
);
}
}
/*****************************************************************
calculate derivatives wrt the parameters
*****************************************************************/
/* Used to calculate the effective variance in effective_error() */
static
void
calc_derivatives
(
const
double
*
par
,
double
*
data
,
double
*
*
deriv
)
{
int
i
,
j
,
k
,
m
;
struct
value
v
;
double
h
;
/* set parameters first */
for
(
i
=
0
;
i
<
num_params
;
i
++
)
Gcomplex
(
par_udv
[
i
],
par
[
i
]
*
scale_params
[
i
],
0.0
);
for
(
i
=
0
;
i
<
num_data
;
i
++
) {
/* loop over data points */
for
(
j
=
0
,
m
=
0
;
j
<
num_indep
;
j
++
) {
/* loop over indep. variables */
double
tmp_high
;
double
tmp_x
;
#ifdef
TWO_SIDE_DIFFERENTIATION
double
tmp_low
;
#endif
/* only calculate derivatives if necessary */
if
(!
err_cols
[
j
])
continue
;
/* set actual dummy variables from file data */
for
(
k
=
0
;
k
<
num_indep
;
k
++
) {
if
(
j
!=
k
)
Gcomplex
(
&
func
.
dummy_values
[
k
],
fit_x
[
i
*
num_indep
+
k
],
0.0
);
}
tmp_x
=
fit_x
[
i
*
num_indep
+
j
];
/* optimal step size */
h
=
GPMAX
(
DELTA
*
fabs
(
tmp_x
),
8
*
1e-8
*
(
fabs
(
tmp_x
)
+
1e-8
));
Gcomplex
(
&
func
.
dummy_values
[
j
],
tmp_x
+
h
,
0.0
);
evaluate_at
(
func
.
at
,
&
v
);
tmp_high
=
real
(
&
v
);
#ifdef
TWO_SIDE_DIFFERENTIATION
Gcomplex
(
&
func
.
dummy_values
[
j
],
tmp_x
-
h
,
0.0
);
evaluate_at
(
func
.
at
,
&
v
);
tmp_low
=
real
(
&
v
);
deriv
[
m
][
i
]
=
(
tmp_high
-
tmp_low
) / (
2
*
h
);
#else
deriv
[
m
][
i
]
=
(
tmp_high
-
data
[
i
]) /
h
;
#endif
m
++
;
}
}
}
/*****************************************************************
handle user interrupts during fit
*****************************************************************/
static
TBOOLEAN
fit_interrupt
()
{
while
(TRUE) {
fputs
(
"\n\n(S)top fit, (C)ontinue, (E)xecute FIT_SCRIPT: "
,
STANDARD
);
#ifdef
_WIN32
WinRaiseConsole
();
#endif
switch
(
getchar
()) {
case
EOF
:
case
's'
:
case
'S'
:
fputs
(
"Stop.\n"
,
STANDARD
);
user_stop
=
TRUE;
return
FALSE;
case
'c'
:
case
'C'
:
fputs
(
"Continue.\n"
,
STANDARD
);
return
TRUE;
case
'e'
:
case
'E'
:{
int
i
;
const
char
*
tmp
=
getfitscript
();
fprintf
(
STANDARD
,
"executing: %s\n"
,
tmp
);
/* FIXME: Shouldn't we also set FIT_STDFIT etc? */
/* set parameters visible to gnuplot */
for
(
i
=
0
;
i
<
num_params
;
i
++
)
Gcomplex
(
par_udv
[
i
],
a
[
i
]
*
scale_params
[
i
],
0.0
);
do_string
(
tmp
);
}
}
}
return
TRUE;
}
/*****************************************************************
determine current setting of FIT_SCRIPT
*****************************************************************/
const
char
*
getfitscript
(
void
)
{
char
*
tmp
;
if
(
fit_script
!=
NULL
)
return
fit_script
;
if
((
tmp
=
getenv
(
FITSCRIPT
))
!=
NULL
)
return
tmp
;
else
return
DEFAULT_CMD
;
}
/*****************************************************************
initial setup for regress()
*****************************************************************/
static
void
regress_init
(
void
)
{
struct
udvt_entry
*
v
;
/* For exporting results to the user */
/* Reset flag describing fit result status */
v
=
add_udv_by_name
(
"FIT_CONVERGED"
);
Ginteger
(
&
v
->
udv_value
,
0
);
/* Ctrl-C now serves as Hotkey */
ctrlc_setup
();
/* HBB 981118: initialize new variable 'user_break' */
user_stop
=
FALSE;
}
/*****************************************************************
finalize regression: print results and set user variables
*****************************************************************/
static
void
regress_finalize
(
int
iter
,
double
chisq
,
double
last_chisq
,
double
lambda
,
double
*
*
covar
)
{
int
i
,
j
;
struct
udvt_entry
*
v
;
/* For exporting results to the user */
int
ndf
;
int
niter
;
double
stdfit
;
double
pvalue
;
double
*
dpar
;
double
*
*
corel
=
NULL
;
TBOOLEAN
covar_invalid
=
FALSE;
/* restore original SIGINT function */
interrupt_setup
();
/* tsm patchset 230: final progress report labels to console */
if
(
fit_verbosity
==
BRIEF
)
fit_show_brief
(
-2
,
chisq
,
chisq
,
a
,
lambda
,
STANDARD
);
/* tsm patchset 230: final progress report to log file */
if
(!
fit_suppress_log
) {
if
(
fit_verbosity
==
VERBOSE
)
fit_show
(
iter
,
chisq
,
last_chisq
,
a
,
lambda
,
log_f
);
else
fit_show_brief
(
iter
,
chisq
,
last_chisq
,
a
,
lambda
,
log_f
);
}
/* test covariance matrix */
if
(
covar
!=
NULL
) {
for
(
i
=
0
;
i
<
num_params
;
i
++
) {
/* diagonal elements must be larger than zero */
if
(
covar
[
i
][
i
] <=
0.0
) {
/* Not a fatal error, but prevent floating point exception later on */
Dblf2
(
"Calculation error: non-positive diagonal element in covar. matrix of parameter '%s'.\n"
,
par_name
[
i
]);
covar_invalid
=
TRUE;
}
}
}
/* HBB 970304: the maxiter patch: */
if
((
maxiter
>
0
)
&&
(
iter
>
maxiter
)) {
Dblf2
(
"\nMaximum iteration count (%d) reached. Fit stopped.\n"
,
maxiter
);
}
else
if
(
user_stop
) {
Dblf2
(
"\nThe fit was stopped by the user after %d iterations.\n"
,
iter
);
}
else
if
(
lambda
>=
MAX_LAMBDA
) {
Dblf2
(
"\nThe maximum lambda = %e was exceeded. Fit stopped.\n"
,
MAX_LAMBDA
);
}
else
if
(
covar_invalid
) {
Dblf2
(
"\nThe covariance matrix is invalid. Fit did not converge properly.\n"
);
}
else
{
Dblf2
(
"\nAfter %d iterations the fit converged.\n"
,
iter
);
v
=
add_udv_by_name
(
"FIT_CONVERGED"
);
Ginteger
(
&
v
->
udv_value
,
1
);
}
/* fit results */
ndf
=
num_data
-
num_params
;
stdfit
=
sqrt
(
chisq
/
ndf
);
pvalue
=
1.
-
chisq_cdf
(
ndf
,
chisq
);
niter
=
iter
;
/* Export these to user-accessible variables */
v
=
add_udv_by_name
(
"FIT_NDF"
);
Ginteger
(
&
v
->
udv_value
,
ndf
);
v
=
add_udv_by_name
(
"FIT_STDFIT"
);
Gcomplex
(
&
v
->
udv_value
,
stdfit
,
0
);
v
=
add_udv_by_name
(
"FIT_WSSR"
);
Gcomplex
(
&
v
->
udv_value
,
chisq
,
0
);
v
=
add_udv_by_name
(
"FIT_P"
);
Gcomplex
(
&
v
->
udv_value
,
pvalue
,
0
);
v
=
add_udv_by_name
(
"FIT_NITER"
);
Ginteger
(
&
v
->
udv_value
,
niter
);
/* Save final parameters. Depending on the backend and
its internal state, the last call_gnuplot may not have been
at the minimum */
for
(
i
=
0
;
i
<
num_params
;
i
++
)
Gcomplex
(
par_udv
[
i
],
a
[
i
]
*
scale_params
[
i
],
0.0
);
/* Set error and covariance variables to zero,
thus making sure they are created. */
if
(
fit_errorvariables
) {
for
(
i
=
0
;
i
<
num_params
;
i
++
)
setvarerr
(
par_name
[
i
],
0.0
);
}
if
(
fit_covarvariables
) {
/* first, remove all previous covariance variables */
del_udv_by_name
(
"FIT_COV_"
, TRUE);
for
(
i
=
0
;
i
<
num_params
;
i
++
) {
for
(
j
=
0
;
j
<
i
;
j
++
) {
setvarcovar
(
par_name
[
i
],
par_name
[
j
],
0.0
);
setvarcovar
(
par_name
[
j
],
par_name
[
i
],
0.0
);
}
setvarcovar
(
par_name
[
i
],
par_name
[
i
],
0.0
);
}
}
/* calculate unscaled parameter errors in dpar[]: */
dpar
=
vec
(
num_params
);
if
((
covar
!=
NULL
)
&&
!
covar_invalid
) {
/* calculate unscaled parameter errors in dpar[]: */
for
(
i
=
0
;
i
<
num_params
;
i
++
) {
dpar
[
i
]
=
sqrt
(
covar
[
i
][
i
]);
}
/* transform covariances into correlations */
corel
=
matr
(
num_params
,
num_params
);
for
(
i
=
0
;
i
<
num_params
;
i
++
) {
/* only lower triangle needs to be handled */
for
(
j
=
0
;
j
<
i
;
j
++
)
corel
[
i
][
j
]
=
covar
[
i
][
j
] / (
dpar
[
i
]
*
dpar
[
j
]);
corel
[
i
][
i
]
=
1.
;
}
}
else
{
/* set all errors to zero if covariance matrix invalid or unavailable */
for
(
i
=
0
;
i
<
num_params
;
i
++
)
dpar
[
i
]
=
0.0
;
}
if
(
fit_errorscaling
||
(
num_errors
==
0
)) {
/* scale parameter errors based on chisq */
double
temp
=
sqrt
(
chisq
/ (
num_data
-
num_params
));
for
(
i
=
0
;
i
<
num_params
;
i
++
)
dpar
[
i
] *=
temp
;
}
/* Save user error variables. */
if
(
fit_errorvariables
) {
for
(
i
=
0
;
i
<
num_params
;
i
++
)
setvarerr
(
par_name
[
i
],
dpar
[
i
]
*
scale_params
[
i
]);
}
/* fill covariance variables if needed */
if
(
fit_covarvariables
&&
(
covar
!=
NULL
)
&&
!
covar_invalid
) {
double
scale
=
(
fit_errorscaling
||
(
num_errors
==
0
)) ?
(
chisq
/ (
num_data
-
num_params
)) :
1.0
;
for
(
i
=
0
;
i
<
num_params
;
i
++
) {
/* only lower triangle needs to be handled */
for
(
j
=
0
;
j
<=
i
;
j
++
) {
double
temp
=
scale
*
scale_params
[
i
]
*
scale_params
[
j
];
setvarcovar
(
par_name
[
i
],
par_name
[
j
],
covar
[
i
][
j
]
*
temp
);
setvarcovar
(
par_name
[
j
],
par_name
[
i
],
covar
[
i
][
j
]
*
temp
);
}
}
View remainder of file in raw view
Back
|
FazBrowse Home
|
New Git URL