summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES7
-rw-r--r--examples/fitting.hs24
-rw-r--r--hmatrix.cabal3
-rw-r--r--lib/Numeric/GSL.hs2
-rw-r--r--lib/Numeric/GSL/Fitting.hs147
-rw-r--r--lib/Numeric/GSL/gsl-aux.c97
-rw-r--r--lib/Numeric/LinearAlgebra/Tests.hs17
7 files changed, 282 insertions, 15 deletions
diff --git a/CHANGES b/CHANGES
index fc089e0..190c0f4 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,8 @@
10.9.2.0
2=======
3
4Added GSL Nonlinear Least-Squares fitting using Levenberg-Marquardt.
5
10.9.1.0 60.9.1.0
2======= 7=======
3 8
@@ -6,7 +11,7 @@
6- Added offset of Vector, allowing fast, noncopy subVector (slice). 11- Added offset of Vector, allowing fast, noncopy subVector (slice).
7 Vector is now identical to Roman Leshchinskiy's Data.Vector.Storable.Vector, 12 Vector is now identical to Roman Leshchinskiy's Data.Vector.Storable.Vector,
8 so we can convert from/to them in O(1). 13 so we can convert from/to them in O(1).
9 14
10- Removed Data.Packed.Convert, see examples/vector.hs 15- Removed Data.Packed.Convert, see examples/vector.hs
11 16
120.8.3.0 170.8.3.0
diff --git a/examples/fitting.hs b/examples/fitting.hs
new file mode 100644
index 0000000..8298c52
--- /dev/null
+++ b/examples/fitting.hs
@@ -0,0 +1,24 @@
1-- nonlinear least-squares fitting
2
3import Numeric.GSL.Fitting
4import Numeric.LinearAlgebra
5
6xs = map return [0 .. 39]
7sigma = 0.1
8ys = map return $ toList $ fromList (map (head . expModel [5,0.1,1]) xs)
9 + scalar sigma * (randomVector 0 Gaussian 40)
10
11dat :: [([Double],[Double],Double)]
12
13dat = zipWith3 (,,) xs ys (repeat sigma)
14
15expModel [a,lambda,b] [t] = [a * exp (-lambda * t) + b]
16
17expModelDer [a,lambda,b] [t] = [[exp (-lambda * t), -t * a * exp(-lambda*t) , 1]]
18
19(sol,path) = fitModel 1E-4 1E-4 20 (resM expModel, resD expModelDer) dat [1,0,0]
20
21main = do
22 print dat
23 print path
24 print sol
diff --git a/hmatrix.cabal b/hmatrix.cabal
index ac38460..5416c09 100644
--- a/hmatrix.cabal
+++ b/hmatrix.cabal
@@ -1,5 +1,5 @@
1Name: hmatrix 1Name: hmatrix
2Version: 0.9.1.0 2Version: 0.9.2.0
3License: GPL 3License: GPL
4License-file: LICENSE 4License-file: LICENSE
5Author: Alberto Ruiz 5Author: Alberto Ruiz
@@ -79,6 +79,7 @@ library
79 Numeric.GSL.Polynomials, 79 Numeric.GSL.Polynomials,
80 Numeric.GSL.Minimization, 80 Numeric.GSL.Minimization,
81 Numeric.GSL.Root, 81 Numeric.GSL.Root,
82 Numeric.GSL.Fitting,
82 Numeric.GSL.ODE, 83 Numeric.GSL.ODE,
83 Numeric.GSL.Vector, 84 Numeric.GSL.Vector,
84 Numeric.GSL, 85 Numeric.GSL,
diff --git a/lib/Numeric/GSL.hs b/lib/Numeric/GSL.hs
index 5442586..ae4c81e 100644
--- a/lib/Numeric/GSL.hs
+++ b/lib/Numeric/GSL.hs
@@ -20,6 +20,7 @@ module Numeric.GSL (
20, module Numeric.GSL.Minimization 20, module Numeric.GSL.Minimization
21, module Numeric.GSL.Root 21, module Numeric.GSL.Root
22, module Numeric.GSL.ODE 22, module Numeric.GSL.ODE
23, module Numeric.GSL.Fitting
23, module Data.Complex 24, module Data.Complex
24, setErrorHandlerOff 25, setErrorHandlerOff
25) where 26) where
@@ -31,6 +32,7 @@ import Numeric.GSL.Polynomials
31import Numeric.GSL.Minimization 32import Numeric.GSL.Minimization
32import Numeric.GSL.Root 33import Numeric.GSL.Root
33import Numeric.GSL.ODE 34import Numeric.GSL.ODE
35import Numeric.GSL.Fitting
34import Data.Complex 36import Data.Complex
35 37
36 38
diff --git a/lib/Numeric/GSL/Fitting.hs b/lib/Numeric/GSL/Fitting.hs
new file mode 100644
index 0000000..6e91b2d
--- /dev/null
+++ b/lib/Numeric/GSL/Fitting.hs
@@ -0,0 +1,147 @@
1{- |
2Module : Numeric.GSL.Fitting
3Copyright : (c) Alberto Ruiz 2010
4License : GPL
5
6Maintainer : Alberto Ruiz (aruiz at um dot es)
7Stability : provisional
8Portability : uses ffi
9
10Nonlinear Least-Squares Fitting
11
12<http://www.gnu.org/software/gsl/manual/html_node/Nonlinear-Least_002dSquares-Fitting.html>
13
14The example program in the GSL manual (see examples/fitting.hs):
15
16@dat = [
17 ([0.0],[6.0133918608118675],0.1),
18 ([1.0],[5.5153769909966535],0.1),
19 ([2.0],[5.261094606015287],0.1),
20 ...
21 ([39.0],[1.0619821710802808],0.1)]
22
23expModel [a,lambda,b] [t] = [a * exp (-lambda * t) + b]
24
25expModelDer [a,lambda,b] [t] = [[exp (-lambda * t), -t * a * exp(-lambda*t) , 1]]
26
27(sol,path) = fitModel 1E-4 1E-4 20 (resM expModel, resD expModelDer) dat [1,0,0]
28
29\> path
30(6><5)
31 [ 1.0, 76.45780563978782, 1.6465931240727802, 1.8147715267618197e-2, 0.6465931240727797
32 , 2.0, 37.683816318260355, 2.858760367632973, 8.092094813253975e-2, 1.4479636296208662
33 , 3.0, 9.5807893736187, 4.948995119561291, 0.11942927999921617, 1.0945766509238248
34 , 4.0, 5.630494933603935, 5.021755718065913, 0.10287787128056883, 1.0338835440862608
35 , 5.0, 5.443976278682909, 5.045204331329302, 0.10405523433131504, 1.019416067207375
36 , 6.0, 5.4439736648994685, 5.045357818922331, 0.10404905846029407, 1.0192487112786812 ]
37\> sol
38[(5.045357818922331,6.027976702418132e-2),
39(0.10404905846029407,3.157045047172834e-3),
40(1.0192487112786812,3.782067731353722e-2)]@
41
42-}
43-----------------------------------------------------------------------------
44
45module Numeric.GSL.Fitting (
46 -- * Levenberg-Marquardt
47 nlFitting, FittingMethod(..),
48 -- * Utilities
49 fitModel, resM, resD
50) where
51
52import Data.Packed.Internal
53import Numeric.LinearAlgebra
54import Foreign
55import Foreign.C.Types(CInt)
56import Numeric.GSL.Internal
57
58-------------------------------------------------------------------------
59
60data FittingMethod = LevenbergMarquardt -- ^ Interface to gsl_multifit_fdfsolver_lmsder. This is a robust and efficient version of the Levenberg-Marquardt algorithm as implemented in the scaled lmder routine in minpack. Minpack was written by Jorge J. More, Burton S. Garbow and Kenneth E. Hillstrom.
61 deriving (Enum,Eq,Show,Bounded)
62
63
64-- | Nonlinear multidimensional least-squares fitting.
65nlFitting :: FittingMethod
66 -> Double -- ^ absolute tolerance
67 -> Double -- ^ relative tolerance
68 -> Int -- ^ maximum number of iterations allowed
69 -> (Vector Double -> Vector Double) -- ^ function to be minimized
70 -> (Vector Double -> Matrix Double) -- ^ Jacobian
71 -> Vector Double -- ^ starting point
72 -> (Vector Double, Matrix Double) -- ^ solution vector and optimization path
73
74nlFitting method epsabs epsrel maxit fun jac xinit = nlFitGen (fi (fromEnum method)) fun jac xinit epsabs epsrel maxit
75
76nlFitGen m f jac xiv epsabs epsrel maxit = unsafePerformIO $ do
77 let p = dim xiv
78 n = dim (f xiv)
79 fp <- mkVecVecfun (aux_vTov (checkdim1 n p . f))
80 jp <- mkVecMatfun (aux_vTom (checkdim2 n p . jac))
81 rawpath <- createMatrix RowMajor maxit (2+p)
82 app2 (c_nlfit m fp jp epsabs epsrel (fi maxit) (fi n)) vec xiv mat rawpath "c_nlfit"
83 let it = round (rawpath @@> (maxit-1,0))
84 path = takeRows it rawpath
85 [sol] = toRows $ dropRows (it-1) path
86 freeHaskellFunPtr fp
87 freeHaskellFunPtr jp
88 return (subVector 2 p sol, path)
89
90foreign import ccall "nlfit"
91 c_nlfit:: CInt -> FunPtr TVV -> FunPtr TVM -> Double -> Double -> CInt -> CInt -> TVM
92
93-------------------------------------------------------
94
95checkdim1 n _p v
96 | dim v == n = v
97 | otherwise = error $ "Error: "++ show n
98 ++ " components expected in the result of the function supplied to nlFitting"
99
100checkdim2 n p m
101 | rows m == n && cols m == p = m
102 | otherwise = error $ "Error: "++ show n ++ "x" ++ show p
103 ++ " Jacobian expected in nlFitting"
104
105------------------------------------------------------------
106
107err model dat vsol = zip sol errs where
108 sol = toList vsol
109 c = max 1 (chi/sqrt (fromIntegral dof))
110 dof = length dat - (rows cov)
111 chi = pnorm PNorm2 (fromList $ cost (fst model) dat sol)
112 js = fromLists $ jacobian (snd model) dat sol
113 cov = inv $ trans js <> js
114 errs = toList $ scalar c * sqrt (takeDiag cov)
115
116
117
118-- | Higher level interface to 'nlFitting' 'LevenbergMarquardt'. The optimization function and
119-- Jacobian are automatically built from a model f vs x = 0 and its derivatives, and a list of
120-- instances x to be fitted.
121
122fitModel :: Double -- ^ absolute tolerance
123 -> Double -- ^ relative tolerance
124 -> Int -- ^ maximum number of iterations allowed
125 -> ([Double] -> x -> [Double], [Double] -> x -> [[Double]]) -- ^ (model, derivatives)
126 -> [x] -- ^ instances
127 -> [Double] -- ^ starting point
128 -> ([(Double, Double)], Matrix Double) -- ^ (solution, error) and optimization path
129fitModel epsabs epsrel maxit model dt xin = (err model dt sol, path) where
130 (sol,path) = nlFitting LevenbergMarquardt epsabs epsrel maxit
131 (fromList . cost (fst model) dt . toList)
132 (fromLists . jacobian (snd model) dt . toList)
133 (fromList xin)
134
135cost model ds vs = concatMap (model vs) ds
136
137jacobian modelDer ds vs = concatMap (modelDer vs) ds
138
139-- | Model-to-residual for association pairs with sigma, to be used with 'fitModel'.
140resM :: ([Double] -> x -> [Double]) -> [Double] -> (x, [Double], Double) -> [Double]
141resM m v = \(x,ys,s) -> zipWith (g s) (m v x) ys where g s a b = (a-b)/s
142
143-- | Associated derivative for 'resM'.
144resD :: ([Double] -> x -> [[Double]]) -> [Double] -> (x, [Double], Double) -> [[Double]]
145resD m v = \(x,_,s) -> map (map (/s)) (m v x)
146
147
diff --git a/lib/Numeric/GSL/gsl-aux.c b/lib/Numeric/GSL/gsl-aux.c
index 0fcde1c..f258fb1 100644
--- a/lib/Numeric/GSL/gsl-aux.c
+++ b/lib/Numeric/GSL/gsl-aux.c
@@ -23,6 +23,7 @@
23#include <gsl/gsl_rng.h> 23#include <gsl/gsl_rng.h>
24#include <gsl/gsl_randist.h> 24#include <gsl/gsl_randist.h>
25#include <gsl/gsl_odeiv.h> 25#include <gsl/gsl_odeiv.h>
26#include <gsl/gsl_multifit_nlin.h>
26#include <string.h> 27#include <string.h>
27#include <stdio.h> 28#include <stdio.h>
28 29
@@ -668,9 +669,10 @@ int root(int method, void f(int, double*, int, double*),
668 669
669// working with the jacobian 670// working with the jacobian
670 671
671typedef struct {int (*f)(int, double*, int, double *); int (*jf)(int, double*, int, int, double*);} Tfjf; 672typedef struct {int (*f)(int, double*, int, double *);
673 int (*jf)(int, double*, int, int, double*);} Tfjf;
672 674
673int f_aux_root(const gsl_vector*x, void *pars, gsl_vector*y) { 675int f_aux(const gsl_vector*x, void *pars, gsl_vector*y) {
674 Tfjf * fjf = ((Tfjf*) pars); 676 Tfjf * fjf = ((Tfjf*) pars);
675 double* p = (double*)calloc(x->size,sizeof(double)); 677 double* p = (double*)calloc(x->size,sizeof(double));
676 double* q = (double*)calloc(y->size,sizeof(double)); 678 double* q = (double*)calloc(y->size,sizeof(double));
@@ -687,20 +689,20 @@ int f_aux_root(const gsl_vector*x, void *pars, gsl_vector*y) {
687 return 0; 689 return 0;
688} 690}
689 691
690int jf_aux_root(const gsl_vector * x, void * pars, gsl_matrix * jac) { 692int jf_aux(const gsl_vector * x, void * pars, gsl_matrix * jac) {
691 Tfjf * fjf = ((Tfjf*) pars); 693 Tfjf * fjf = ((Tfjf*) pars);
692 double* p = (double*)calloc(x->size,sizeof(double)); 694 double* p = (double*)calloc(x->size,sizeof(double));
693 double* q = (double*)calloc((x->size)*(x->size),sizeof(double)); 695 double* q = (double*)calloc((jac->size1)*(jac->size2),sizeof(double));
694 int i,j,k; 696 int i,j,k;
695 for(k=0;k<x->size;k++) { 697 for(k=0;k<x->size;k++) {
696 p[k] = gsl_vector_get(x,k); 698 p[k] = gsl_vector_get(x,k);
697 } 699 }
698 700
699 (fjf->jf)(x->size,p,x->size,x->size,q); 701 (fjf->jf)(x->size,p,jac->size1,jac->size2,q);
700 702
701 k=0; 703 k=0;
702 for(i=0;i<x->size;i++) { 704 for(i=0;i<jac->size1;i++) {
703 for(j=0;j<x->size;j++){ 705 for(j=0;j<jac->size2;j++){
704 gsl_matrix_set(jac,i,j,q[k++]); 706 gsl_matrix_set(jac,i,j,q[k++]);
705 } 707 }
706 } 708 }
@@ -709,9 +711,9 @@ int jf_aux_root(const gsl_vector * x, void * pars, gsl_matrix * jac) {
709 return 0; 711 return 0;
710} 712}
711 713
712int fjf_aux_root(const gsl_vector * x, void * pars, gsl_vector * f, gsl_matrix * g) { 714int fjf_aux(const gsl_vector * x, void * pars, gsl_vector * f, gsl_matrix * g) {
713 f_aux_root(x,pars,f); 715 f_aux(x,pars,f);
714 jf_aux_root(x,pars,g); 716 jf_aux(x,pars,g);
715 return 0; 717 return 0;
716} 718}
717 719
@@ -723,9 +725,9 @@ int rootj(int method, int f(int, double*, int, double*),
723 DEBUGMSG("root_fjf"); 725 DEBUGMSG("root_fjf");
724 gsl_multiroot_function_fdf my_func; 726 gsl_multiroot_function_fdf my_func;
725 // extract function from pars 727 // extract function from pars
726 my_func.f = f_aux_root; 728 my_func.f = f_aux;
727 my_func.df = jf_aux_root; 729 my_func.df = jf_aux;
728 my_func.fdf = fjf_aux_root; 730 my_func.fdf = fjf_aux;
729 my_func.n = xin; 731 my_func.n = xin;
730 Tfjf stfjf; 732 Tfjf stfjf;
731 stfjf.f = f; 733 stfjf.f = f;
@@ -781,8 +783,77 @@ int rootj(int method, int f(int, double*, int, double*),
781 OK 783 OK
782} 784}
783 785
786//-------------- non linear least squares fitting -------------------
787
788int nlfit(int method, int f(int, double*, int, double*),
789 int jac(int, double*, int, int, double*),
790 double epsabs, double epsrel, int maxit, int p,
791 KRVEC(xi), RMAT(sol)) {
792 REQUIRES(solr == maxit && solc == 2+xin,BAD_SIZE);
793 DEBUGMSG("nlfit");
794 const gsl_multifit_fdfsolver_type *T;
795 gsl_multifit_fdfsolver *s;
796 gsl_multifit_function_fdf my_f;
797 // extract function from pars
798 my_f.f = f_aux;
799 my_f.df = jf_aux;
800 my_f.fdf = fjf_aux;
801 my_f.n = p;
802 my_f.p = xin; // !!!!
803 Tfjf stfjf;
804 stfjf.f = f;
805 stfjf.jf = jac;
806 my_f.params = &stfjf;
807 size_t iter = 0;
808 int status;
809
810 KDVVIEW(xi);
811 //DMVIEW(cov);
812
813 switch(method) {
814 case 0 : { T = gsl_multifit_fdfsolver_lmsder; break; }
815 default: ERROR(BAD_CODE);
816 }
817
818 s = gsl_multifit_fdfsolver_alloc (T, my_f.n, my_f.p);
819 gsl_multifit_fdfsolver_set (s, &my_f, V(xi));
820
821 do { status = gsl_multifit_fdfsolver_iterate (s);
822
823 solp[iter*solc+0] = iter+1;
824 solp[iter*solc+1] = gsl_blas_dnrm2 (s->f);
825
826 int k;
827 for(k=0;k<xin;k++) {
828 solp[iter*solc+k+2] = gsl_vector_get(s->x,k);
829 }
830
831 iter++;
832 if (status) /* check if solver is stuck */
833 break;
834
835 status = gsl_multifit_test_delta (s->dx, s->x, epsabs, epsrel);
836 }
837 while (status == GSL_CONTINUE && iter <= maxit);
838
839 int i,j;
840 for (i=iter; i<solr; i++) {
841 solp[i*solc+0] = iter;
842 for(j=1;j<solc;j++) {
843 solp[i*solc+j]=0.;
844 }
845 }
846
847 //gsl_multifit_covar (s->J, 0.0, M(cov));
848
849 gsl_multifit_fdfsolver_free (s);
850 OK
851}
852
853
784////////////////////////////////////////////////////// 854//////////////////////////////////////////////////////
785 855
856
786#define RAN(C,F) case C: { for(k=0;k<rn;k++) { rp[k]= F(gen); }; OK } 857#define RAN(C,F) case C: { for(k=0;k<rn;k++) { rp[k]= F(gen); }; OK }
787 858
788int random_vector(int seed, int code, RVEC(r)) { 859int random_vector(int seed, int code, RVEC(r)) {
diff --git a/lib/Numeric/LinearAlgebra/Tests.hs b/lib/Numeric/LinearAlgebra/Tests.hs
index 3f7c847..f8f8bd5 100644
--- a/lib/Numeric/LinearAlgebra/Tests.hs
+++ b/lib/Numeric/LinearAlgebra/Tests.hs
@@ -143,6 +143,22 @@ odeTest = utest "ode" (last (toLists sol) ~~ [-1.7588880332411019, 8.36434890871
143 143
144--------------------------------------------------------------------- 144---------------------------------------------------------------------
145 145
146fittingTest = utest "levmar" ok
147 where
148 xs = map return [0 .. 39]
149 sigma = 0.1
150 ys = map return $ toList $ fromList (map (head . expModel [5,0.1,1]) xs)
151 + scalar sigma * (randomVector 0 Gaussian 40)
152 dat = zipWith3 (,,) xs ys (repeat sigma)
153
154 expModel [a,lambda,b] [t] = [a * exp (-lambda * t) + b]
155 expModelDer [a,lambda,_b] [t] = [[exp (-lambda * t), -t * a * exp(-lambda*t) , 1]]
156
157 sol = fst $ fitModel 1E-4 1E-4 20 (resM expModel, resD expModelDer) dat [1,0,0]
158 ok = and (zipWith f sol [5,0.1,1]) where f (x,d) r = abs (x-r)<2*d
159
160---------------------------------------------------------------------
161
146randomTestGaussian = c :~1~: snd (meanCov dat) where 162randomTestGaussian = c :~1~: snd (meanCov dat) where
147 a = (3><3) [1,2,3, 163 a = (3><3) [1,2,3,
148 2,4,0, 164 2,4,0,
@@ -292,6 +308,7 @@ runTests n = do
292 && rank ((2><3)[1,0,0,1,7*eps,0]) == 2 308 && rank ((2><3)[1,0,0,1,7*eps,0]) == 2
293 , utest "block" $ fromBlocks [[ident 3,0],[0,ident 4]] == (ident 7 :: CM) 309 , utest "block" $ fromBlocks [[ident 3,0],[0,ident 4]] == (ident 7 :: CM)
294 , odeTest 310 , odeTest
311 , fittingTest
295 ] 312 ]
296 return () 313 return ()
297 314