packages feed

levmar-0.1: LevMar/Fitting.hs

{-# LANGUAGE ScopedTypeVariables #-}

--------------------------------------------------------------------------------
-- |
-- Module      :  LevMar.Fitting
-- Copyright   :  (c) 2009 Roel van Dijk & Bas van Dijk
-- License     :  BSD-style (see the file LICENSE)
--
-- Maintainer  :  vandijk.roel@gmail.com, v.dijk.bas@gmail.com
-- Stability   :  Experimental
--
-- This module provides the Levenberg-Marquardt algorithm specialised
-- for curve-fitting.
--
-- For additional documentation see the documentation of the levmar C
-- library which this library is based on:
-- <http://www.ics.forth.gr/~lourakis/levmar/>
--
--------------------------------------------------------------------------------

module LevMar.Fitting
    ( -- * Model & Jacobian.
      Model
    , Jacobian

      -- * Levenberg-Marquardt algorithm.
    , LMA.LevMarable
    , levmar

    , LMA.LinearConstraints
    , LMA.noLinearConstraints
    , LMA.Matrix

    -- * Minimization options.
    , LMA.Options(..)
    , LMA.defaultOpts

      -- * Output
    , LMA.Info(..)
    , LMA.StopReason(..)
    , LMA.CovarMatrix

    , LMA.LevMarError(..)

      -- *Type-level machinery
    , Z, S, Nat
    , SizedList(..)
    , NFunction
    , ComposeN
    ) where


import qualified LevMar as LMA

import TypeLevelNat (Z, S, Nat, witnessNat)
import SizedList    (SizedList)
import NFunction    (NFunction, ComposeN, compose)


--------------------------------------------------------------------------------
-- Model & Jacobian.
--------------------------------------------------------------------------------

{- | A function from @n@ parameters of type @r@ and an x-value of type
@a@ to a value of type @r@.

For example, the quadratic function @f(x) = a*x^2 + b*x + c@ can be
written as:

@
type N3 = 'S' ('S' ('S' 'Z'))

quad :: 'Num' r => 'Model' N3 r r
quad a b c x = a*x^2 + b*x + c
@
-}
type Model n r a = NFunction n r (a -> r)

{- | The jacobian of the 'Model' function. Expressed as a function from
@n@ parameters of type @r@ and an x-value of type @a@ to a vector
of @n@ values of type @r@.

See: <http://en.wikipedia.org/wiki/Jacobian_matrix_and_determinant>

For example, the jacobian of the quadratic function @f(x) = a*x^2 +
b*x + c@ can be written as:

@
type N3 = 'S' ('S' ('S' 'Z'))

quadJacob :: 'Num' r => 'Jacobian' N3 r r
quadJacob _ _ _ x =   x^2   -- with respect to a
                  ::: x     -- with respect to b
                  ::: 1     -- with respect to c
                  ::: 'Nil'
@

Notice you don't have to differentiate for @x@.
-}
type Jacobian n r a = NFunction n r (a -> SizedList n r)


--------------------------------------------------------------------------------
-- Levenberg-Marquardt algorithm.
--------------------------------------------------------------------------------

-- | The Levenberg-Marquardt algorithm specialised for curve-fitting.
levmar :: forall n k r a. (Nat n, ComposeN n, Nat k, LMA.LevMarable r)
       => (Model n r a)                          -- ^ Model
       -> Maybe (Jacobian n r a)                 -- ^ Optional jacobian
       -> SizedList n r                          -- ^ Initial parameters
       -> [(a, r)]                               -- ^ Samples
       -> Integer                                -- ^ Maximum number of iterations
       -> LMA.Options r                          -- ^ Options
       -> Maybe (SizedList n r)                  -- ^ Optional lower bounds
       -> Maybe (SizedList n r)                  -- ^ Optional upper bounds
       -> Maybe (LMA.LinearConstraints k n r)    -- ^ Optional linear constraints
       -> Maybe (SizedList n r)                  -- ^ Optional weights
       -> Either LMA.LevMarError (SizedList n r, LMA.Info r, LMA.CovarMatrix n r)
levmar model mJac params samples = LMA.levmar (convertModel model)
                                              (fmap convertJacob mJac)
                                              params
                                              ys
    where
      (xs, ys) = unzip samples

      convertModel :: Model n r a -> LMA.Model n r
      convertModel = compose (witnessNat :: n) (undefined :: r)
                             (\(f :: a -> r) -> map f xs)

      convertJacob :: Jacobian n r a -> LMA.Jacobian n r
      convertJacob = compose (witnessNat :: n) (undefined :: r)
                             (\(f :: a -> SizedList n r) -> map f xs)


-- The End ---------------------------------------------------------------------