Tcl wrapper for C optimization procedures (v0.2)

ExamplesTop, Main, Index

This section contains examples of usage of fitting procedures of the package. List of availible examples:

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:

ticklEcharts !!!