-
Notifications
You must be signed in to change notification settings - Fork 0
/
mpfit.pro
2258 lines (2059 loc) · 78.8 KB
/
mpfit.pro
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
;+
; NAME:
; MPFIT
;
; AUTHOR:
; Craig B. Markwardt, NASA/GSFC Code 662, Greenbelt, MD 20770
; UPDATED VERSIONs can be found on my WEB PAGE:
; http://cow.physics.wisc.edu/~craigm/idl/idl.html
;
; PURPOSE:
; Perform Levenberg-Marquardt least-squares minimization (MINPACK-1)
;
; MAJOR TOPICS:
; Curve and Surface Fitting
;
; CALLING SEQUENCE:
; parms = MPFIT(MYFUNCT, start_parms, FUNCTARGS=fcnargs, NFEV=nfev,
; MAXITER=maxiter, ERRMSG=errmsg, NPRINT=nprint, QUIET=quiet,
; FTOL=ftol, XTOL=xtol, GTOL=gtol, NITER=niter,
; STATUS=status, ITERPROC=iterproc, ITERARGS=iterargs,
; COVAR=covar, PERROR=perror, BESTNORM=bestnorm,
; PARINFO=parinfo)
;
; DESCRIPTION:
;
; MPFIT uses the Levenberg-Marquardt technique to solve the
; least-squares problem. In its typical use, MPFIT will be used to
; fit a user-supplied function (the "model") to user-supplied data
; points (the "data") by adjusting a set of parameters.
;
; For example, a researcher may think that a set of observed data
; points is best modelled with a Gaussian curve. A Gaussian curve is
; parameterized by its mean, standard deviation and normalization.
; MPFIT will, within certain constraints, find the set of parameters
; which best fits the data. The fit is "best" in the least-squares
; sense; that is, the sum of the weighted squared differences between
; the model and data is minimized.
;
; The Levenberg-Marquardt technique is a particular strategy for
; iteratively searching for the best fit. This particular
; implementation is drawn from MINPACK-1 (see NETLIB), and seems to
; be more robust than routines provided with IDL. This version
; allows upper and lower bounding constraints to be placed on each
; parameter, or the parameter can be held fixed.
;
; The IDL user-supplied function should return an array of weighted
; deviations between model and data. In a typical scientific problem
; the residuals should be weighted so that each deviate has a
; gaussian sigma of 1.0. If X represents values of the independent
; variable, Y represents a measurement for each value of X, and ERR
; represents the error in the measurements, then the deviates could
; be calculated as follows:
;
; DEVIATES = (Y - F(X)) / ERR
;
; where F is the analytical function representing the model. You are
; recommended to use the convenience functions MPFITFUN and
; MPFITEXPR, which are driver functions that calculate the deviates
; for you. If ERR are the 1-sigma uncertainties in Y, then
;
; TOTAL( DEVIATES^2 )
;
; will be the total chi-squared value. MPFIT will minimize the
; chi-square value. The values of X, Y and ERR are passed through
; MPFIT to the user-supplied function via the FUNCTARGS keyword.
;
; MPFIT does not perform more general optimization tasks. See TNMIN
; instead. MPFIT is customized, based on MINPACK-1, to the
; least-squares minimization problem.
;
; In the search for the best-fit solution, MPFIT calculates
; derivatives numerically via a finite difference approximation, by
; default. The user-supplied function need not calculate the
; derivatives explicitly, but can if AUTODERIVATIVE=0 is passed.
;
; INPUTS:
; MYFUNCT - a string variable containing the name of the function to
; be minimized. The function should return the weighted
; deviations between the model and the data. It should be
; declared in the following way (or in some equivalent):
;
; FUNCTION MYFUNCT, p, dp, X=x, Y=y, ERR=err
; ; Parameter values are passed in "p"
; model = F(x)
; ; Optionally compute derivatives -- NOT REQUIRED
; if n_params() GT 1 then dp = ... ; NOT REQUIRED
;
; ; Calculation of deviations occurs here
; return, (y-model)/err
; END
;
; The keyword parameters X, Y, and ERR in the example
; above are suggestive but not required. Any parameters
; can be passed to MYFUNCT by using the FUNCTARGS keyword
; to MPFIT. Use MPFITFUN and MPFITEXPR if you need ideas
; on how to do that. The function *must* accept a
; parameter list, P, and *at least* one keyword.
;
; In general there are no restrictions on the number of
; dimensions in X, Y or ERR. However the deviates *must*
; be returned in a one-dimensional array, and must have
; the same type (float or double) as the input arrays.
;
; You may wish to compute your own derivatives (rather
; than allowing MPFIT to compute them for you), if for
; example, it is straightforward to do it analytically.
; If so, then you should (1) pass AUTODERIVATIVE=0 (see
; below), (2) your function should compute the
; derivatives, and (3) return them in the parameter "dp".
; "dp" should be an m x n array, where m is the number of
; data points and n is the number of parameters. dp(i,j)
; is the derivative at the ith point with respect to the
; jth parameter. As a practical matter, it is often
; sufficient and even faster to allow MPFIT to calculate
; the derivatives numerically, and so AUTODERIVATIVE=0 is
; not necessary.
;
; The derivatives with respect to fixed parameters are
; ignored; zero is an appropriate value to insert for
; those derivatives. If the data is higher than one
; dimensional, then the *last* dimension should be the
; parameter dimension. Example: fitting a 50x50 image,
; "dp" should be 50x50xNPAR.
;
; User functions can automatically decide whether they are
; expected to provide derivatives if N_PARAMS() is greater
; than one.
;
; User functions may also indicate a fatal error condition
; using the ERROR_CODE common block variable, as described
; below under the MPFIT_ERROR common block definition.
;
; START_PARAMS - An array of starting values for each of the
; parameters of the model. The number of parameters
; should be fewer than the number of measurements.
; Also, the parameters should have the same data type
; as the measurements (double is preferred).
;
; This parameter is optional if the PARINFO keyword
; is used (but see PARINFO). The PARINFO keyword
; provides a mechanism to fix or constrain individual
; parameters. If both START_PARAMS and PARINFO are
; passed, then the starting *value* is taken from
; START_PARAMS, but the *constraints* are taken from
; PARINFO.
;
; RETURNS:
;
; Returns the array of best-fit parameters.
;
;
; KEYWORD PARAMETERS:
;
; AUTODERIVATIVE - If this is set, derivatives of the function will
; be computed automatically via a finite
; differencing procedure. If not set, then MYFUNCT
; must provide the (analytical) derivatives.
; Default: set (=1)
; NOTE: to supply your own analytical derivatives,
; explicitly pass AUTODERIVATIVE=0
;
; BESTNORM - the value of the summed squared residuals for the
; returned parameter values.
;
; COVAR - the covariance matrix for the set of parameters returned
; by MPFIT. The matrix is NxN where N is the number of
; parameters. The square root of the diagonal elements
; gives the formal 1-sigma statistical errors on the
; parameters IF errors were treated "properly" in MYFUNC.
; Parameter errors are also returned in PERROR.
;
; To compute the correlation matrix, PCOR, use this example:
; IDL> PCOR = COV * 0
; IDL> FOR i = 0, n-1 DO FOR j = 0, n-1 DO $
; PCOR(i,j) = COV(i,j)/sqrt(COV(i,i)*COV(j,j))
;
; If NOCOVAR is set or MPFIT terminated abnormally, then
; COVAR is set to a scalar with value !VALUES.D_NAN.
;
; ERRMSG - a string error or warning message is returned.
;
; FASTNORM - set this keyword to select a faster algorithm to
; compute sum-of-square values internally. For systems
; with large numbers of data points, the standard
; algorithm can become prohibitively slow because it
; cannot be vectorized well. By setting this keyword,
; MPFIT will run faster, but it will be more prone to
; floating point overflows and underflows. Thus, setting
; this keyword may sacrifice some stability in the
; fitting process.
;
; FTOL - a nonnegative input variable. Termination occurs when both
; the actual and predicted relative reductions in the sum of
; squares are at most FTOL (and STATUS is accordingly set to
; 1 or 3). Therefore, FTOL measures the relative error
; desired in the sum of squares. Default: 1D-10
;
; FUNCTARGS - A structure which contains the parameters to be passed
; to the user-supplied function specified by MYFUNCT via
; the _EXTRA mechanism. This is the way you can pass
; additional data to your user-supplied function without
; using common blocks.
;
; Consider the following example:
; if FUNCTARGS = { XVAL:[1.D,2,3], YVAL:[1.D,4,9],
; ERRVAL:[1.D,1,1] }
; then the user supplied function should be declared
; like this:
; FUNCTION MYFUNCT, P, XVAL=x, YVAL=y, ERRVAL=err
;
; By default, no extra parameters are passed to the
; user-supplied function, but your function should
; accept *at least* one keyword parameter. [ This is to
; accomodate a limitation in IDL's _EXTRA
; parameter-passing mechanism. ]
;
; GTOL - a nonnegative input variable. Termination occurs when the
; cosine of the angle between fvec and any column of the
; jacobian is at most GTOL in absolute value (and STATUS is
; accordingly set to 4). Therefore, GTOL measures the
; orthogonality desired between the function vector and the
; columns of the jacobian. Default: 1D-10
;
; ITERARGS - The keyword arguments to be passed to ITERPROC via the
; _EXTRA mechanism. This should be a structure, and is
; similar in operation to FUNCTARGS.
; Default: no arguments are passed.
;
; ITERPROC - The name of a procedure to be called upon each NPRINT
; iteration of the MPFIT routine. It should be declared
; in the following way:
;
; PRO ITERPROC, MYFUNCT, p, iter, fnorm, FUNCTARGS=fcnargs, $
; PARINFO=parinfo, QUIET=quiet, ...
; ; perform custom iteration update
; END
;
; ITERPROC must either accept all three keyword
; parameters (FUNCTARGS, PARINFO and QUIET), or at least
; accept them via the _EXTRA keyword.
;
; MYFUNCT is the user-supplied function to be minimized,
; P is the current set of model parameters, ITER is the
; iteration number, and FUNCTARGS are the arguments to be
; passed to MYFUNCT. FNORM should be the
; chi-squared value. QUIET is set when no textual output
; should be printed. See below for documentation of
; PARINFO.
;
; In implementation, ITERPROC can perform updates to the
; terminal or graphical user interface, to provide
; feedback while the fit proceeds. If the fit is to be
; stopped for any reason, then ITERPROC should set the
; common block variable ERROR_CODE to negative value (see
; MPFIT_ERROR common block below). In principle,
; ITERPROC should probably not modify the parameter
; values, because it may interfere with the algorithm's
; stability. In practice it is allowed.
;
; Default: an internal routine is used to print the
; parameter values.
;
; ITERSTOP - Set this keyword if you wish to be able to stop the
; fitting by hitting Control-G on the keyboard. This
; only works if you use the default ITERPROC.
;
; MAXITER - The maximum number of iterations to perform. If the
; number is exceeded, then the STATUS value is set to 5
; and MPFIT returns.
; Default: 200 iterations
;
; NFEV - the number of MYFUNCT function evaluations performed.
;
; NITER - the number of iterations completed.
;
; NOCOVAR - set this keyword to prevent the calculation of the
; covariance matrix before returning (see COVAR)
;
; NPRINT - The frequency with which ITERPROC is called. A value of
; 1 indicates that ITERPROC is called with every iteration,
; while 2 indicates every other iteration, etc. Note that
; several Levenberg-Marquardt attempts can be made in a
; single iteration.
; Default value: 1
;
; PARINFO - Provides a mechanism for more sophisticated constraints
; to be placed on parameter values. When PARINFO is not
; passed, then it is assumed that all parameters are free
; and unconstrained. Values in PARINFO are never
; modified during a call to MPFIT.
;
; PARINFO should be an array of structures, one for each
; parameter. Each parameter is associated with one
; element of the array, in numerical order. The structure
; can have the following entries (none are required):
;
; .VALUE - the starting parameter value (but see
; START_PARAMS above).
;
; .FIXED - a boolean value, whether the parameter is to
; be held fixed or not. Fixed parameters are
; not varied by MPFIT, but are passed on to
; MYFUNCT for evaluation.
;
; .LIMITED - a two-element boolean array. If the
; first/second element is set, then the parameter is
; bounded on the lower/upper side. A parameter can be
; bounded on both sides. Both LIMITED and LIMITS must
; be given together.
;
; .LIMITS - a two-element float or double array. Gives
; the parameter limits on the lower and upper sides,
; respectively. Zero, one or two of these values can
; be set, depending on the values of LIMITED. Both
; LIMITED and LIMITS must be given together.
;
; .STEP - the step size to be used in calculating the
; numerical derivatives. If set to zero, then the
; step size is computed automatically.
;
; .PARNAME - a string, giving the name of the parameter. The
; fitting code of MPFIT does not use this tag in any
; way. However, the default ITERPROC will print the
; parameter name if available.
;
; .TIED - a string expression which "ties" the
; parameter to other free or fixed parameters. Any
; expression involving constants and the parameter
; array P are permitted. Example: if parameter 2 is
; always to be twice parameter 1 then use the
; following: parinfo(2).tied = '2 * P(1)'. Since they
; are totally constrained, tied parameters are
; considered to be fixed; no errors are computed for
; them. [ NOTE: the PARNAME can't be used in
; expressions. ]
;
; Other tag values can also be given in the structure, but
; they are ignored.
;
; Example:
; parinfo = replicate({value:0.D, fixed:0, limited:[0,0], $
; limits:[0.D,0]}, 5)
; parinfo(0).fixed = 1
; parinfo(4).limited(0) = 1
; parinfo(4).limits(0) = 50.D
; parinfo(*).value = [5.7D, 2.2, 500., 1.5, 2000.]
;
; A total of 5 parameters, with starting values of 5.7,
; 2.2, 500, 1.5, and 2000 are given. The first parameter
; is fixed at a value of 5.7, and the last parameter is
; constrained to be above 50.
;
; Default value: all parameters are free and unconstrained.
;
; PERROR - The formal 1-sigma errors in each parameter, computed
; from the covariance matrix. If a parameter is held
; fixed, or if it touches a boundary, then the error is
; reported as zero.
;
; If the fit is unweighted (i.e. no errors were given, or
; the weights were uniformly set to unity), then PERROR
; will probably not represent the true parameter
; uncertainties.
;
; *If* you can assume that the true reduced chi-squared
; value is unity -- meaning that the fit is implicitly
; assumed to be of good quality -- then the estimated
; parameter uncertainties can be computed by scaling PERROR
; by the measured chi-squared value.
;
; DOF = N_ELEMENTS(X) - N_ELEMENTS(PARMS) ; deg of freedom
; PCERROR = PERROR * SQRT(BESTNORM / DOF) ; scaled uncertainties
;
; QUIET - set this keyword when no textual output should be printed
; by MPFIT
;
; STATUS - an integer status code is returned. All values other
; than zero can represent success. It can have one of the
; following values:
;
; 0 improper input parameters.
;
; 1 both actual and predicted relative reductions
; in the sum of squares are at most FTOL.
;
; 2 relative error between two consecutive iterates
; is at most XTOL
;
; 3 conditions for STATUS = 1 and STATUS = 2 both hold.
;
; 4 the cosine of the angle between fvec and any
; column of the jacobian is at most GTOL in
; absolute value.
;
; 5 the maximum number of iterations has been reached
;
; 6 FTOL is too small. no further reduction in
; the sum of squares is possible.
;
; 7 XTOL is too small. no further improvement in
; the approximate solution x is possible.
;
; 8 GTOL is too small. fvec is orthogonal to the
; columns of the jacobian to machine precision.
;
; XTOL - a nonnegative input variable. Termination occurs when the
; relative error between two consecutive iterates is at most
; XTOL (and STATUS is accordingly set to 2 or 3). Therefore,
; XTOL measures the relative error desired in the approximate
; solution. Default: 1D-10
;
;
; EXAMPLE:
;
; p0 = [5.7D, 2.2, 500., 1.5, 2000.]
; fa = {X:x, Y:y, ERR:err}
; p = mpfit('MYFUNCT', p0, functargs=fa)
;
; Minimizes sum of squares of MYFUNCT. MYFUNCT is called with the X,
; Y, and ERR keyword parameters that are given by FUNCTARGS. The
; resulting parameter values are returned in p.
;
;
; COMMON BLOCKS:
;
; COMMON MPFIT_ERROR, ERROR_CODE
;
; User routines may stop the fitting process at any time by
; setting an error condition. This condition may be set in either
; the user's model computation routine (MYFUNCT), or in the
; iteration procedure (ITERPROC).
;
; To stop the fitting, the above common block must be declared,
; and ERROR_CODE must be set to a negative number. After the user
; procedure or function returns, MPFIT checks the value of this
; common block variable and exits immediately if the error
; condition has been set. By default the value of ERROR_CODE is
; zero, indicating a successful function/procedure call.
;
; COMMON MPFIT_PROFILE
; COMMON MPFIT_MACHAR
; COMMON MPFIT_CONFIG
;
; These are undocumented common blocks are used internally by
; MPFIT and may change in future implementations.
;
; REFERENCES:
;
; MINPACK-1, Jorge More', available from netlib (www.netlib.org).
; "Optimization Software Guide," Jorge More' and Stephen Wright,
; SIAM, *Frontiers in Applied Mathematics*, Number 14.
;
; MODIFICATION HISTORY:
; Translated from MINPACK-1 in FORTRAN, Apr-Jul 1998, CM
; Fixed bug in parameter limits (x vs xnew), 04 Aug 1998, CM
; Added PERROR keyword, 04 Aug 1998, CM
; Added COVAR keyword, 20 Aug 1998, CM
; Added NITER output keyword, 05 Oct 1998
; D.L Windt, Bell Labs, [email protected];
; Made each PARINFO component optional, 05 Oct 1998 CM
; Analytical derivatives allowed via AUTODERIVATIVE keyword, 09 Nov 1998
; Parameter values can be tied to others, 09 Nov 1998
; Fixed small bugs (Wayne Landsman), 24 Nov 1998
; Added better exception error reporting, 24 Nov 1998 CM
; Cosmetic documentation changes, 02 Jan 1999 CM
; Changed definition of ITERPROC to be consistent with TNMIN, 19 Jan 1999 CM
; Fixed bug when AUTDERIVATIVE=0. Incorrect sign, 02 Feb 1999 CM
; Added keyboard stop to MPFIT_DEFITER, 28 Feb 1999 CM
; Cosmetic documentation changes, 14 May 1999 CM
; IDL optimizations for speed & FASTNORM keyword, 15 May 1999 CM
; Tried a faster version of mpfit_enorm, 30 May 1999 CM
; Changed web address to cow.physics.wisc.edu, 14 Jun 1999 CM
; Found malformation of FDJAC in MPFIT for 1 parm, 03 Aug 1999 CM
; Factored out user-function call into MPFIT_CALL. It is possible,
; but currently disabled, to call procedures. The calling format
; is similar to CURVEFIT, 25 Sep 1999, CM
; Slightly changed mpfit_tie to be less intrusive, 25 Sep 1999, CM
; Fixed some bugs associated with tied parameters in mpfit_fdjac, 25
; Sep 1999, CM
; Reordered documentation; now alphabetical, 02 Oct 1999, CM
; Added QUERY keyword for more robust error detection in drivers, 29
; Oct 1999, CM
; Documented PERROR for unweighted fits, 03 Nov 1999, CM
; Split out MPFIT_RESETPROF to aid in profiling, 03 Nov 1999, CM
; Some profiling and speed optimization, 03 Nov 1999, CM
; Worst offenders, in order: fdjac2, qrfac, qrsolv, enorm.
; fdjac2 depends on user function, qrfac and enorm seem to be
; fully optimized. qrsolv probably could be tweaked a little, but
; is still <10% of total compute time.
; Made sure that !err was set to 0 in MPFIT_DEFITER, 10 Jan 2000, CM
; Fixed small inconsistency in setting of QANYLIM, 28 Jan 2000, CM
; Added PARINFO field RELSTEP, 28 Jan 2000, CM
; Converted to MPFIT_ERROR common block for indicating error
; conditions, 28 Jan 2000, CM
; Corrected scope of MPFIT_ERROR common block, CM, 07 Mar 2000
; Minor speed improvement in MPFIT_ENORM, CM 26 Mar 2000
; Corrected case where ITERPROC changed parameter values and
; parameter values were TIED, CM 26 Mar 2000
; Changed MPFIT_CALL to modify NFEV automatically, and to support
; user procedures more, CM 26 Mar 2000
; Copying permission terms have been liberalized, 26 Mar 2000, CM
; Catch zero value of zero a(j,lj) in MPFIT_QRFAC, 20 Jul 2000, CM
; (thanks to David Schlegel <[email protected]>)
; MPFIT_SETMACHAR is called only once at init; only one common block
; is created (MPFIT_MACHAR); it is now a structure; removed almost
; all CHECK_MATH calls for compatibility with IDL5 and !EXCEPT;
; profiling data is now in a structure too; noted some
; mathematical discrepancies in Linux IDL5.0, 17 Nov 2000, CM
;
;-
; Copyright (C) 1997-2000, Craig Markwardt
; This software is provided as is without any warranty whatsoever.
; Permission to use, copy, modify, and distribute modified or
; unmodified copies is granted, provided this copyright and disclaimer
; are included unchanged.
;-
pro mpfit_dummy
;; Enclose in a procedure so these are not defined in the main level
FORWARD_FUNCTION mpfit_fdjac2, mpfit_enorm, mpfit_lmpar, mpfit_covar, $
mpfit, mpfit_call
COMMON mpfit_error, error_code ;; For error passing to user function
COMMON mpfit_config, mpconfig ;; For internal error configrations
end
;; Reset profiling registers for another run. By default, and when
;; uncommented, the profiling registers simply accumulate.
pro mpfit_resetprof
common mpfit_profile, mpfit_profile_vals
mpfit_profile_vals = { status: 1L, fdjac2: 0D, lmpar: 0D, mpfit: 0D, $
qrfac: 0D, qrsolv: 0D, enorm: 0D}
return
end
;; Following are machine constants that can be loaded once. I have
;; found that bizarre underflow messages can be produced in each call
;; to MACHAR(), so this structure minimizes the number of calls to
;; one.
pro mpfit_setmachar, double=isdouble
common mpfit_profile, profvals
if n_elements(profvals) EQ 0 then mpfit_resetprof
common mpfit_machar, mpfit_machar_vals
;; In earlier versions of IDL, MACHAR itself could produce a load of
;; error messages. We try to mask some of that out here.
if (!version.release) LT 5 then dummy = check_math(1, 1)
mch = 0.
mch = machar(double=keyword_set(isdouble))
dmachep = mch.eps
dmaxnum = mch.xmax
dminnum = mch.xmin
dmaxlog = alog(mch.xmax)
dminlog = alog(mch.xmin)
if keyword_set(isdouble) then $
dmaxgam = 171.624376956302725D $
else $
dmaxgam = 171.624376956302725
drdwarf = sqrt(dminnum*1.5) * 10
drgiant = sqrt(dmaxnum) * 0.1
mpfit_machar_vals = {machep: dmachep, maxnum: dmaxnum, minnum: dminnum, $
maxlog: dmaxlog, minlog: dminlog, maxgam: dmaxgam, $
rdwarf: drdwarf, rgiant: drgiant}
if (!version.release) LT 5 then dummy = check_math(0, 0)
return
end
;; Call user function or procedure, with _EXTRA or not, with
;; derivatives or not.
function mpfit_call, fcn, x, fjac, ptied_=ptied, nfev_=nfev, _EXTRA=extra
on_error, 2
if n_params() EQ 3 then if n_elements(ptied) GT 0 then mpfit_tie, x, ptied
;; Decide whether we are calling a procedure or function
common mpfit_config, mpconfig
if n_elements(mpconfig) GT 0 then $
if mpconfig.proc then proc = 1 else proc = 0
if n_elements(nfev) LE 0 then nfev = 0L
nfev = nfev + 1
if proc then begin
if n_params() EQ 3 then begin
if n_elements(extra) GT 0 then $
call_procedure, fcn, x, f, fjac, _EXTRA=extra $
else $
call_procedure, fcn, x, f, fjac
endif else begin
if n_elements(extra) GT 0 then $
call_procedure, fcn, x, f, _EXTRA=extra $
else $
call_procedure, fcn, x, f
endelse
endif else begin
if n_params() EQ 3 then begin
if n_elements(extra) GT 0 then $
f = call_function(fcn, x, fjac, _EXTRA=extra) $
else $
f = call_function(fcn, x, fjac)
endif else begin
if n_elements(extra) GT 0 then $
f = call_function(fcn, x, _EXTRA=extra) $
else $
f = call_function(fcn, x)
endelse
endelse
return, f
end
function mpfit_fdjac2, fcn, x, fvec, step, ulimited, ulimit, ptied, $
iflag=iflag, epsfcn=epsfcn, nfev=nfev, autoderiv=autoderiv, $
FUNCTARGS=fcnargs, xall=xall, ifree=ifree, dstep=dstep
common mpfit_machar, machvals
common mpfit_profile, profvals
common mpfit_error, mperr
; prof_start = systime(1)
MACHEP0 = machvals.machep
DWARF = machvals.minnum
if n_elements(epsfcn) EQ 0 then epsfcn = MACHEP0
if n_elements(xall) EQ 0 then xall = x
if n_elements(ifree) EQ 0 then ifree = lindgen(n_elements(xall))
if n_elements(step) EQ 0 then step = x * 0
eps = sqrt(max([epsfcn, MACHEP0]));
m = n_elements(fvec)
n = n_elements(x)
;; Compute analytical derivative if requested
if NOT keyword_set(autoderiv) then begin
mperr = 0
fjac = 0 ;; Initialize it so FCN will know to compute derivatives
fp = mpfit_call(fcn, xall, fjac, nfev_=nfev, _EXTRA=fcnargs)
iflag = mperr
nall = n_elements(xall)
if n_elements(fjac) NE m*nall then begin
message, 'ERROR: Derivative matrix was not computed properly.', /info
iflag = 1
; profvals.fdjac2 = profvals.fdjac2 + (systime(1) - prof_start)
return, 0
endif
;; This definition is consistent with CURVEFIT
;; Sign error found, thanks to
;; Jesus Fernandez <[email protected]>
fjac = reform(-temporary(fjac), m, nall, /overwrite)
;; Select only the free parameters
if n_elements(ifree) LT nall then $
fjac = reform(fjac(*,ifree), m, n, /overwrite)
; profvals.fdjac2 = profvals.fdjac2 + (systime(1) - prof_start)
return, fjac
endif
fjac = make_array(m, n, value=fvec(0)*0)
fjac = reform(fjac, m, n, /overwrite)
h = eps * abs(x)
;; if STEP is given, use that
if n_elements(step) GT 0 then begin
wh = where(step GT 0, ct)
if ct GT 0 then h(wh) = step(wh)
endif
;; if relative step is given, use that
if n_elements(dstep) GT 0 then begin
wh = where(dstep GT 0, ct)
if ct GT 0 then h(wh) = abs(dstep(wh)*x(wh))
endif
;; In case any of the step values are zero
wh = where(h EQ 0, ct)
if ct GT 0 then h(wh) = eps
;; if LIMITS specified, then respect those
ct = 0 & if n_elements(ulimited) GT 0 AND n_elements(ulimit) GT 0 then $
wh = where(ulimited AND (x GT ulimit-h), ct)
if ct GT 0 then h(wh) = -h(wh)
;; Loop through parameters, computing the derivative for each
for j=0L, n-1 do begin
xp = xall
xp(ifree(j)) = xp(ifree(j)) + h(j)
mperr = 0
fp = mpfit_call(fcn, xp, ptied_=ptied, nfev_=nfev, _EXTRA=fcnargs)
iflag = mperr
if iflag LT 0 then return, !values.d_nan
;; Note optimization fjac(0:*,j)
fjac(0,j) = (fp-fvec)/h(j)
endfor
; profvals.fdjac2 = profvals.fdjac2 + (systime(1) - prof_start)
return, fjac
end
function mpfit_enorm, vec
;; NOTE: it turns out that, for systems that have a lot of data
;; points, this routine is a big computing bottleneck. The extended
;; computations that need to be done cannot be effectively
;; vectorized. The introduction of the FASTNORM configuration
;; parameter allows the user to select a faster routine, which is
;; based on TOTAL() alone.
common mpfit_profile, profvals
; prof_start = systime(1)
common mpfit_config, mpconfig
; Very simple-minded sum-of-squares
if n_elements(mpconfig) GT 0 then if mpconfig.fastnorm then begin
ans = sqrt(total(vec^2))
goto, TERMINATE
endif
common mpfit_machar, machvals
agiant = machvals.rgiant / n_elements(vec)
adwarf = machvals.rdwarf * n_elements(vec)
;; This is hopefully a compromise between speed and robustness.
;; Need to do this because of the possibility of over- or underflow.
mx = max(vec, min=mn)
mx = max(abs([mx,mn]))
if mx EQ 0 then return, vec(0)*0
if mx GT agiant OR mx LT adwarf then ans = mx * sqrt(total((vec/mx)^2))$
else ans = sqrt( total(vec^2) )
TERMINATE:
; profvals.enorm = profvals.enorm + (systime(1) - prof_start)
return, ans
end
; **********
;
; subroutine qrfac
;
; this subroutine uses householder transformations with column
; pivoting (optional) to compute a qr factorization of the
; m by n matrix a. that is, qrfac determines an orthogonal
; matrix q, a permutation matrix p, and an upper trapezoidal
; matrix r with diagonal elements of nonincreasing magnitude,
; such that a*p = q*r. the householder transformation for
; column k, k = 1,2,...,min(m,n), is of the form
;
; t
; i - (1/u(k))*u*u
;
; where u has zeros in the first k-1 positions. the form of
; this transformation and the method of pivoting first
; appeared in the corresponding linpack subroutine.
;
; the subroutine statement is
;
; subroutine qrfac(m,n,a,lda,pivot,ipvt,lipvt,rdiag,acnorm,wa)
;
; where
;
; m is a positive integer input variable set to the number
; of rows of a.
;
; n is a positive integer input variable set to the number
; of columns of a.
;
; a is an m by n array. on input a contains the matrix for
; which the qr factorization is to be computed. on output
; the strict upper trapezoidal part of a contains the strict
; upper trapezoidal part of r, and the lower trapezoidal
; part of a contains a factored form of q (the non-trivial
; elements of the u vectors described above).
;
; lda is a positive integer input variable not less than m
; which specifies the leading dimension of the array a.
;
; pivot is a logical input variable. if pivot is set true,
; then column pivoting is enforced. if pivot is set false,
; then no column pivoting is done.
;
; ipvt is an integer output array of length lipvt. ipvt
; defines the permutation matrix p such that a*p = q*r.
; column j of p is column ipvt(j) of the identity matrix.
; if pivot is false, ipvt is not referenced.
;
; lipvt is a positive integer input variable. if pivot is false,
; then lipvt may be as small as 1. if pivot is true, then
; lipvt must be at least n.
;
; rdiag is an output array of length n which contains the
; diagonal elements of r.
;
; acnorm is an output array of length n which contains the
; norms of the corresponding columns of the input matrix a.
; if this information is not needed, then acnorm can coincide
; with rdiag.
;
; wa is a work array of length n. if pivot is false, then wa
; can coincide with rdiag.
;
; subprograms called
;
; minpack-supplied ... dpmpar,enorm
;
; fortran-supplied ... dmax1,dsqrt,min0
;
; argonne national laboratory. minpack project. march 1980.
; burton s. garbow, kenneth e. hillstrom, jorge j. more
;
; **********
pro mpfit_qrfac, a, ipvt, rdiag, acnorm, pivot=pivot
sz = size(a)
m = sz(1)
n = sz(2)
common mpfit_machar, machvals
common mpfit_profile, profvals
; prof_start = systime(1)
MACHEP0 = machvals.machep
DWARF = machvals.minnum
;; Compute the initial column norms and initialize arrays
acnorm = make_array(n, value=a(0)*0)
for j = 0L, n-1 do $
acnorm(j) = mpfit_enorm(a(*,j))
rdiag = acnorm
wa = rdiag
if keyword_set(pivot) then ipvt = lindgen(n)
aold = a
;; Reduce a to r with householder transformations
minmn = min([m,n])
for j = 0L, minmn-1 do begin
if NOT keyword_set(pivot) then goto, HOUSE1
;; Bring the column of largest norm into the pivot position
rmax = max(rdiag(j:*))
kmax = where(rdiag(j:*) EQ rmax, ct) + j
if ct LE 0 then goto, HOUSE1
kmax = kmax(0)
;; Exchange rows via the pivot only. Avoid actually exchanging
;; the rows, in case there is lots of memory transfer. The
;; exchange occurs later, within the body of MPFIT, after the
;; extraneous columns of the matrix have been shed.
if kmax NE j then begin
temp = ipvt(j) & ipvt(j) = ipvt(kmax) & ipvt(kmax) = temp
rdiag(kmax) = rdiag(j)
wa(kmax) = wa(j)
endif
HOUSE1:
;; Compute the householder transformation to reduce the jth
;; column of a to a multiple of the jth unit vector
lj = ipvt(j)
ajj = a(j:*,lj)
ajnorm = mpfit_enorm(ajj)
if ajnorm EQ 0 then goto, NEXT_ROW
if a(j,j) LT 0 then ajnorm = -ajnorm
ajj = ajj / ajnorm
ajj(0) = ajj(0) + 1
;; *** Note optimization a(j:*,j)
a(j,lj) = ajj
;; Apply the transformation to the remaining columns
;; and update the norms
;; NOTE to SELF: tried to optimize this by removing the loop,
;; but it actually got slower. Reverted to "for" loop to keep
;; it simple.
if j+1 LT n then begin
for k=j+1, n-1 do begin
lk = ipvt(k)
ajk = a(j:*,lk)
;; *** Note optimization a(j:*,lk)
;; (corrected 20 Jul 2000)
if a(j,lj) NE 0 then $
a(j,lk) = ajk - ajj * total(ajk*ajj)/a(j,lj)
if keyword_set(pivot) AND rdiag(k) NE 0 then begin
temp = a(j,lk)/rdiag(k)
rdiag(k) = rdiag(k) * sqrt((1.-temp^2) > 0)
temp = rdiag(k)/wa(k)
if 0.05D*temp*temp LE MACHEP0 then begin
rdiag(k) = mpfit_enorm(a(j+1:*,lk))
wa(k) = rdiag(k)
endif
endif
endfor
endif
NEXT_ROW:
rdiag(j) = -ajnorm
endfor
; profvals.qrfac = profvals.qrfac + (systime(1) - prof_start)
return
end
; **********
;
; subroutine qrsolv
;
; given an m by n matrix a, an n by n diagonal matrix d,
; and an m-vector b, the problem is to determine an x which
; solves the system
;
; a*x = b , d*x = 0 ,
;
; in the least squares sense.
;
; this subroutine completes the solution of the problem
; if it is provided with the necessary information from the
; qr factorization, with column pivoting, of a. that is, if
; a*p = q*r, where p is a permutation matrix, q has orthogonal
; columns, and r is an upper triangular matrix with diagonal
; elements of nonincreasing magnitude, then qrsolv expects
; the full upper triangle of r, the permutation matrix p,
; and the first n components of (q transpose)*b. the system
; a*x = b, d*x = 0, is then equivalent to
;
; t t
; r*z = q *b , p *d*p*z = 0 ,
;
; where x = p*z. if this system does not have full rank,
; then a least squares solution is obtained. on output qrsolv
; also provides an upper triangular matrix s such that
;
; t t t
; p *(a *a + d*d)*p = s *s .
;
; s is computed within qrsolv and may be of separate interest.
;
; the subroutine statement is
;
; subroutine qrsolv(n,r,ldr,ipvt,diag,qtb,x,sdiag,wa)
;
; where
;
; n is a positive integer input variable set to the order of r.
;
; r is an n by n array. on input the full upper triangle
; must contain the full upper triangle of the matrix r.
; on output the full upper triangle is unaltered, and the
; strict lower triangle contains the strict upper triangle
; (transposed) of the upper triangular matrix s.
;
; ldr is a positive integer input variable not less than n
; which specifies the leading dimension of the array r.
;
; ipvt is an integer input array of length n which defines the
; permutation matrix p such that a*p = q*r. column j of p
; is column ipvt(j) of the identity matrix.
;
; diag is an input array of length n which must contain the
; diagonal elements of the matrix d.
;
; qtb is an input array of length n which must contain the first
; n elements of the vector (q transpose)*b.
;
; x is an output array of length n which contains the least
; squares solution of the system a*x = b, d*x = 0.
;
; sdiag is an output array of length n which contains the
; diagonal elements of the upper triangular matrix s.
;
; wa is a work array of length n.
;
; subprograms called
;
; fortran-supplied ... dabs,dsqrt
;
; argonne national laboratory. minpack project. march 1980.
; burton s. garbow, kenneth e. hillstrom, jorge j. more
;
pro mpfit_qrsolv, r, ipvt, diag, qtb, x, sdiag
sz = size(r)
m = sz(1)
n = sz(2)
delm = lindgen(n) * (m+1) ;; Diagonal elements of r