ExamplesTop, Main, Index
This section contains examples of usage of fitting procedures of the package. List of availible examples:
- Fit to sum of sinusoidal functions - "examples/sinfit.tcl" file
Fit to sum of sinusoidal functionsTop, Main, Index
First step is to import packages:
package require tclopt package require ticklecharts set ::ticklecharts::theme "dark" namespace import ::tcl::mathfunc::* namespace import ::tclopt::*
Then we set seed for rand
function with srand
:
srand 10
Define function for generating random number from min value to max value:
proc randFloat {min max} { return [expr {rand()*($max-$min)+$min}] }
As data for fit we generate 100 points with step 0.01 using next function:
randFloat(0.9, 1.1) ⋅ (sin(1.5 ⋅ x) + sin(11 ⋅ x) + sin(6 ⋅ x))
Data generation code:
for {set i 0} {$i<100} {incr i} { set xi [= {$i*0.01}] lappend x $xi lappend y [= {[randFloat 0.9 1.1]*(sin(1.5*$xi)+sin(11*$xi)+sin(6*$xi))}] } set pdata [dcreate x $x y $y]
Next we define function we want to minimize:
proc sinfunc {xall pdata args} { set x [dget $pdata x] set y [dget $pdata y] foreach xVal $x yVal $y { set f [= {sin([@ $xall 0]*$xVal)+sin([@ $xall 1]*$xVal)+sin([@ $xall 2]*$xVal)}] lappend fvec [= {$yVal-$f}] lappend fval $f } return [dcreate fvec $fvec fval $fval] }
On input we have pdata
dictionary that contains x and y points for calculating residuals. Residuals are calculated as such:
fvec = y - (sin(p0 ⋅ x) + sin(p1 ⋅ x) + sin(p2 ⋅ x))
Also we save the function values to calculate the fitted data, and return dictionary containing both lists. For ::tclopt::Mpfit::run procedure we must provide the dictionary with fvec
key.
We have 3 parameters, and we can make optimization faster if we provide resasonable limits to parameters values. For that task we create ::tclopt::ParameterMpfit objects and set boundaries for each parameter as [0,20], and then this list will be the input to [::tclopt::mpfit] procedure).
set xInitial [list 3.0 8.0 1.0] set par0 [ParameterMpfit new a [@ $xInitial 0] -lowlim 0 -uplim 20] set par1 [ParameterMpfit new b [@ $xInitial 1] -lowlim 0 -uplim 20] set par2 [ParameterMpfit new c [@ $xInitial 2] -lowlim 0 -uplim 20]
We define optimizer object ::tclopt::Mpfit and added parameters objects to it:
set optimizer [Mpfit new -funct sinfunc -m 100 -pdata $pdata] $optimizer addPars $par0 $par1 $par2
We provide name of our function as an -funct
argument, define the number of point -m 100
and data for calculating residuals -pdata $pdata
. Also, the important fact: in the order we add parameters to optimizer object the parameters will be passed to minimizing function, and method ::tclopt::Mpfit::run returns final values list under key 'x' in the same order.
Now we are ready to call optimization routine with method ::tclopt::Mpfit::run and collect the results:
set result [$optimizer run] set yinitial [dget [sinfunc $xInitial $pdata] fval] set yfinal [dget [sinfunc [dget $result x] $pdata] fval]
The resulted dictionary contains the solution vector x
with values of parameters, and miscellanious information about fitting process, part of it we can print:
puts "Chi^2 final: [format "%3f" [dget $result bestnorm]]" puts "Chi^2 initial: [format "%3f" [dget $result orignorm]]" puts "number of interations: [dget $result niter]" puts "number of function evaluation: [dget $result nfev]" set i -1 foreach xerr [dget $result xerror] xVal [dget $result x] xini $xInitial { puts [format "p[incr i]: %.3f ± %.3f" $xVal $xerr] }
Results are:
Chi^2 final: 0.321206 Chi^2 initial: 106.649503 number of interations: 8 number of function evaluation: 30 p0: 6.012 ± 0.468 p1: 11.013 ± 0.407 p2: 1.507 ± 0.484
Now we can plot fitted curve, initial data and curve before fitting:
set chart [ticklecharts::chart new] $chart Xaxis -name "x" -minorTick {show "True"} -min 0 -max 1 -type "value" -splitLine {show "True"} $chart Yaxis -name "y" -minorTick {show "True"} -min 0 -max 2.5 -type "value" -splitLine {show "True"} $chart SetOptions -title {} -legend {} -tooltip {} -animation "False" -backgroundColor "#212121" -toolbox {feature {dataZoom {yAxisIndex "none"}}} $chart Add "lineSeries" -data [lmap xVal $x yVal $y {list $xVal $yVal}] -showAllSymbol "nothing" -name "Data" $chart Add "lineSeries" -data [lmap xVal $x yVal $yinitial {list $xVal $yVal}] -showAllSymbol "nothing" -name "Initial" $chart Add "lineSeries" -data [lmap xVal $x yVal $yfinal {list $xVal $yVal}] -showAllSymbol "nothing" -name "Fitted" set fbasename [file rootname [file tail [info script]]] $chart Render -outfile [file normalize [file join html_charts $fbasename.html]] -height 800px
Results are: