mpfit

mpfit overview

mpfit is a wrapper around the cMPFIT library for Nim.

Usage of the library is centered around a single fit procedure, with signature:

proc fit*[T](f: FuncProto[T], pS: openArray[T], x, y, ey: openArray[T]): (seq[T], mp_result) =

the first argument is a user defined function (see below), the following arguments are:

  • pS: the first guess for the parameters
  • x: data for x
  • y: data for y
  • ey: errors for y

The FuncProto[T] type is simply a specific signature for a proc, which takes a sequence of parameters and a value. It is the user defined function to be fitted.

Internally the function and the data is stored in a VarStruct object:

type
  VarStruct*[T] = ref object
    x: seq[T]
    y: seq[T]
    ey: seq[T]
    f: FuncProto[T]

This is done, because the C library accepts a function to be fitted of the form:

mp_func* = proc (m: cint; n: cint; x: ptr cdouble; fvec: ptr cdouble;
                 dvec: ptr ptr cdouble; private_data: var pointer): cint {.cdecl.}

where m is the number of data points, n the number of parameters, x a pointer to the sequence of parameters, fvec a pointer to the sequence of deviates and dvec allows for user computed derivates (which are currently not implemented).

The last parameter private_data is an opaque pointer and the reason for the VarStruct object. We hand the user data and user function to the mp_func via a cast to a pointer. A funcImpl wrapper proc of the signature mp_func casts this opaque pointer back to VarStruct[float] (yes, currently it explicitly casts back to float, so the allowed signature is actually only FuncProto[float] for the time being), and calls the user function with the data computing the deviates:

for i in 0 ..< m:
  f = ff(pCall, x[i])
  dy[i] = (y[i] - f) / ey[i]

where ff is the user function and pCall the parameters as a seq[float] (since they have to be handed as a ptr cdouble we have to convert to seq[float] first).

For an example see the https://github.com/Vindaar/nim-mpfit/blob/master/README.org.

Types

FuncProto[T] = proc (p: seq[T]; x: T): T
MpConfig = object
  ftol*: float ## NOTE: the user may set the value explicitly; OR, if the passed
               ##     value is zero, then the "Default" value will be substituted by
               ##     mpfit().
               ## Relative chi-square convergence criterium  Default: 1e-10
  xtol*: float               ## Relative parameter convergence criterium   Default: 1e-10
  gtol*: float               ## Orthogonality convergence criterium        Default: 1e-10
  epsfcn*: float             ## Finite derivative step size                Default: MP_MACHEP0
  stepFactor*: float         ## Initial step bound                         Default: 100.0
  covtol*: float             ## Range tolerance for covariance calculation Default: 1e-14
  maxiter*: int ## Maximum number of iterations.  If maxiter == MP_NO_ITER,
                ##   then basic error checking is done, and parameter
                ##   errors/covariances are estimated based on input
                ##   parameter values, but no fitting iterations are done.
                ##                                            Default: 200
                ## 
  maxfev*: int ## Maximum number of function evaluations, or 0 for no limit
               ##                                            Default: 0 (no limit)
  nprint*: int               ## Default: 1
  doUserScale*: bool         ## Scale variables by user values?
                             ## 		     1 = yes, user scale values in diag;
                             ## 		     0 = no, variables scaled internally (Default)
  noFiniteCheck*: bool       ## Disable check for infinite quantities from user?
                             ## 			0 = do not perform check (Default)
                             ## 			1 = perform check
  

Procs

func chiSq(res: mp_result): float {....raises: [], tags: [].}
given an mp_result, return the chi^2 of the fit
func cov(res: mp_result): seq[seq[float]] {....raises: [], tags: [].}
given an mp_result, return the covariance matrix of the fit parameters as a nested seq of shape [npar, npar]
proc echoResult(x: openArray[float]; res: mp_result;
                xact: openArray[float] = @[]) {....raises: [ValueError], tags: [].}

A convenience proc to echo the fit parameters and their errors as well as the properties of the fit, e.g. chi^2 etc.

The first argument x are the final resulting fit paramters (the first return value of fit, xact are the actual values (e.g.. your possibly known parameters you want to compare with in case the fit was only a cross check) and res is the mp_result object, the second return value of the fit proc.

func error(res: mp_result): seq[float] {....raises: [], tags: [].}
given an mp_result, return the errors of the fit parameters
proc fit[T](userFunc: FuncProto[T]; pS: openArray[T]; x, y, ey: openArray[T];
            bounds: seq[tuple[l, u: float]] = @[]; config = none[MpConfig]()): (
    seq[T], mp_result)

The actual fit procedure, which needs to be called by the user. userFunc is the function to be fitted to the data x, y and ey, where ey is the error on y.

It's possible to set bounds on the fit parameters, by handing a seq (one element per parameter) of lower l and upper u bound values.

func formatValue(f: float; precision: int): string {....raises: [], tags: [].}
proc pretty(x: openArray[float]; res: mp_result; xact: openArray[float] = @[];
            unicode = true; precision = -1; prefix = "  "): string {.
    ...raises: [ValueError], tags: [].}

A convenience proc to echo the fit parameters and their errors as well as the properties of the fit, e.g. chi^2 etc.

The first argument x are the final resulting fit paramters (the first return value of fit, xact are the actual values (e.g.. your possibly known parameters you want to compare with in case the fit was only a cross check) and res is the mp_result object, the second return value of the fit proc.

func reducedChiSq(res: mp_result): float {....raises: [], tags: [].}
given an mp_result, return the reduced chi^2 of the fit, i.e.
reducedChiSq = \chi^2 / d.o.f
             = \chi^2 / (# data points - # parameters)