diff options
Diffstat (limited to 'lib')
-rw-r--r-- | lib/Numeric/GSL/Fitting.hs | 72 | ||||
-rw-r--r-- | lib/Numeric/GSL/gsl-aux.c | 1 | ||||
-rw-r--r-- | lib/Numeric/LinearAlgebra/Tests.hs | 12 |
3 files changed, 59 insertions, 26 deletions
diff --git a/lib/Numeric/GSL/Fitting.hs b/lib/Numeric/GSL/Fitting.hs index 6e91b2d..b1f3a32 100644 --- a/lib/Numeric/GSL/Fitting.hs +++ b/lib/Numeric/GSL/Fitting.hs | |||
@@ -14,17 +14,17 @@ Nonlinear Least-Squares Fitting | |||
14 | The example program in the GSL manual (see examples/fitting.hs): | 14 | The example program in the GSL manual (see examples/fitting.hs): |
15 | 15 | ||
16 | @dat = [ | 16 | @dat = [ |
17 | ([0.0],[6.0133918608118675],0.1), | 17 | ([0.0],([6.0133918608118675],0.1)), |
18 | ([1.0],[5.5153769909966535],0.1), | 18 | ([1.0],([5.5153769909966535],0.1)), |
19 | ([2.0],[5.261094606015287],0.1), | 19 | ([2.0],([5.261094606015287],0.1)), |
20 | ... | 20 | ... |
21 | ([39.0],[1.0619821710802808],0.1)] | 21 | ([39.0],([1.0619821710802808],0.1))] |
22 | 22 | ||
23 | expModel [a,lambda,b] [t] = [a * exp (-lambda * t) + b] | 23 | expModel [a,lambda,b] [t] = [a * exp (-lambda * t) + b] |
24 | 24 | ||
25 | expModelDer [a,lambda,b] [t] = [[exp (-lambda * t), -t * a * exp(-lambda*t) , 1]] | 25 | expModelDer [a,lambda,b] [t] = [[exp (-lambda * t), -t * a * exp(-lambda*t) , 1]] |
26 | 26 | ||
27 | (sol,path) = fitModel 1E-4 1E-4 20 (resM expModel, resD expModelDer) dat [1,0,0] | 27 | (sol,path) = fitModelScaled 1E-4 1E-4 20 (expModel, expModelDer) dat [1,0,0] |
28 | 28 | ||
29 | \> path | 29 | \> path |
30 | (6><5) | 30 | (6><5) |
@@ -46,7 +46,7 @@ module Numeric.GSL.Fitting ( | |||
46 | -- * Levenberg-Marquardt | 46 | -- * Levenberg-Marquardt |
47 | nlFitting, FittingMethod(..), | 47 | nlFitting, FittingMethod(..), |
48 | -- * Utilities | 48 | -- * Utilities |
49 | fitModel, resM, resD | 49 | fitModelScaled, fitModel |
50 | ) where | 50 | ) where |
51 | 51 | ||
52 | import Data.Packed.Internal | 52 | import Data.Packed.Internal |
@@ -57,7 +57,8 @@ import Numeric.GSL.Internal | |||
57 | 57 | ||
58 | ------------------------------------------------------------------------- | 58 | ------------------------------------------------------------------------- |
59 | 59 | ||
60 | data 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. | 60 | data FittingMethod = LevenbergMarquardtScaled -- ^ 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 | | LevenbergMarquardt -- ^ This is an unscaled version of the lmder algorithm. The elements of the diagonal scaling matrix D are set to 1. This algorithm may be useful in circumstances where the scaled version of lmder converges too slowly, or the function is already scaled appropriately. | ||
61 | deriving (Enum,Eq,Show,Bounded) | 62 | deriving (Enum,Eq,Show,Bounded) |
62 | 63 | ||
63 | 64 | ||
@@ -104,32 +105,52 @@ checkdim2 n p m | |||
104 | 105 | ||
105 | ------------------------------------------------------------ | 106 | ------------------------------------------------------------ |
106 | 107 | ||
107 | err model dat vsol = zip sol errs where | 108 | err (model,deriv) dat vsol = zip sol errs where |
108 | sol = toList vsol | 109 | sol = toList vsol |
109 | c = max 1 (chi/sqrt (fromIntegral dof)) | 110 | c = max 1 (chi/sqrt (fromIntegral dof)) |
110 | dof = length dat - (rows cov) | 111 | dof = length dat - (rows cov) |
111 | chi = pnorm PNorm2 (fromList $ cost (fst model) dat sol) | 112 | chi = pnorm PNorm2 (fromList $ cost (resMs model) dat sol) |
112 | js = fromLists $ jacobian (snd model) dat sol | 113 | js = fromLists $ jacobian (resDs deriv) dat sol |
113 | cov = inv $ trans js <> js | 114 | cov = inv $ trans js <> js |
114 | errs = toList $ scalar c * sqrt (takeDiag cov) | 115 | errs = toList $ scalar c * sqrt (takeDiag cov) |
115 | 116 | ||
116 | 117 | ||
117 | 118 | ||
119 | -- | Higher level interface to 'nlFitting' 'LevenbergMarquardtScaled'. The optimization function and | ||
120 | -- Jacobian are automatically built from a model f vs x = y and its derivatives, and a list of | ||
121 | -- instances (x, (y,sigma)) to be fitted. | ||
122 | |||
123 | fitModelScaled | ||
124 | :: Double -- ^ absolute tolerance | ||
125 | -> Double -- ^ relative tolerance | ||
126 | -> Int -- ^ maximum number of iterations allowed | ||
127 | -> ([Double] -> x -> [Double], [Double] -> x -> [[Double]]) -- ^ (model, derivatives) | ||
128 | -> [(x, ([Double], Double))] -- ^ instances | ||
129 | -> [Double] -- ^ starting point | ||
130 | -> ([(Double, Double)], Matrix Double) -- ^ (solution, error) and optimization path | ||
131 | fitModelScaled epsabs epsrel maxit (model,deriv) dt xin = (err (model,deriv) dt sol, path) where | ||
132 | (sol,path) = nlFitting LevenbergMarquardtScaled epsabs epsrel maxit | ||
133 | (fromList . cost (resMs model) dt . toList) | ||
134 | (fromLists . jacobian (resDs deriv) dt . toList) | ||
135 | (fromList xin) | ||
136 | |||
137 | |||
138 | |||
118 | -- | Higher level interface to 'nlFitting' 'LevenbergMarquardt'. The optimization function and | 139 | -- | 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 | 140 | -- Jacobian are automatically built from a model f vs x = y and its derivatives, and a list of |
120 | -- instances x to be fitted. | 141 | -- instances (x,y) to be fitted. |
121 | 142 | ||
122 | fitModel :: Double -- ^ absolute tolerance | 143 | fitModel :: Double -- ^ absolute tolerance |
123 | -> Double -- ^ relative tolerance | 144 | -> Double -- ^ relative tolerance |
124 | -> Int -- ^ maximum number of iterations allowed | 145 | -> Int -- ^ maximum number of iterations allowed |
125 | -> ([Double] -> x -> [Double], [Double] -> x -> [[Double]]) -- ^ (model, derivatives) | 146 | -> ([Double] -> x -> [Double], [Double] -> x -> [[Double]]) -- ^ (model, derivatives) |
126 | -> [x] -- ^ instances | 147 | -> [(x, [Double])] -- ^ instances |
127 | -> [Double] -- ^ starting point | 148 | -> [Double] -- ^ starting point |
128 | -> ([(Double, Double)], Matrix Double) -- ^ (solution, error) and optimization path | 149 | -> ([Double], Matrix Double) -- ^ solution and optimization path |
129 | fitModel epsabs epsrel maxit model dt xin = (err model dt sol, path) where | 150 | fitModel epsabs epsrel maxit (model,deriv) dt xin = (toList sol, path) where |
130 | (sol,path) = nlFitting LevenbergMarquardt epsabs epsrel maxit | 151 | (sol,path) = nlFitting LevenbergMarquardt epsabs epsrel maxit |
131 | (fromList . cost (fst model) dt . toList) | 152 | (fromList . cost (resM model) dt . toList) |
132 | (fromLists . jacobian (snd model) dt . toList) | 153 | (fromLists . jacobian (resD deriv) dt . toList) |
133 | (fromList xin) | 154 | (fromList xin) |
134 | 155 | ||
135 | cost model ds vs = concatMap (model vs) ds | 156 | cost model ds vs = concatMap (model vs) ds |
@@ -137,11 +158,18 @@ cost model ds vs = concatMap (model vs) ds | |||
137 | jacobian modelDer ds vs = concatMap (modelDer vs) ds | 158 | jacobian modelDer ds vs = concatMap (modelDer vs) ds |
138 | 159 | ||
139 | -- | Model-to-residual for association pairs with sigma, to be used with 'fitModel'. | 160 | -- | Model-to-residual for association pairs with sigma, to be used with 'fitModel'. |
140 | resM :: ([Double] -> x -> [Double]) -> [Double] -> (x, [Double], Double) -> [Double] | 161 | resMs :: ([Double] -> x -> [Double]) -> [Double] -> (x, ([Double], Double)) -> [Double] |
141 | resM m v = \(x,ys,s) -> zipWith (g s) (m v x) ys where g s a b = (a-b)/s | 162 | resMs m v = \(x,(ys,s)) -> zipWith (g s) (m v x) ys where g s a b = (a-b)/s |
142 | 163 | ||
143 | -- | Associated derivative for 'resM'. | 164 | -- | Associated derivative for 'resMs'. |
144 | resD :: ([Double] -> x -> [[Double]]) -> [Double] -> (x, [Double], Double) -> [[Double]] | 165 | resDs :: ([Double] -> x -> [[Double]]) -> [Double] -> (x, ([Double], Double)) -> [[Double]] |
145 | resD m v = \(x,_,s) -> map (map (/s)) (m v x) | 166 | resDs m v = \(x,(_,s)) -> map (map (/s)) (m v x) |
146 | 167 | ||
168 | -- | Model-to-residual for association pairs, to be used with 'fitModel'. It is equivalent | ||
169 | -- to 'resMs' with all sigmas = 1. | ||
170 | resM :: ([Double] -> x -> [Double]) -> [Double] -> (x, [Double]) -> [Double] | ||
171 | resM m v = \(x,ys) -> zipWith g (m v x) ys where g a b = a-b | ||
147 | 172 | ||
173 | -- | Associated derivative for 'resM'. | ||
174 | resD :: ([Double] -> x -> [[Double]]) -> [Double] -> (x, [Double]) -> [[Double]] | ||
175 | resD m v = \(x,_) -> m v x | ||
diff --git a/lib/Numeric/GSL/gsl-aux.c b/lib/Numeric/GSL/gsl-aux.c index f258fb1..f5a95f4 100644 --- a/lib/Numeric/GSL/gsl-aux.c +++ b/lib/Numeric/GSL/gsl-aux.c | |||
@@ -812,6 +812,7 @@ int nlfit(int method, int f(int, double*, int, double*), | |||
812 | 812 | ||
813 | switch(method) { | 813 | switch(method) { |
814 | case 0 : { T = gsl_multifit_fdfsolver_lmsder; break; } | 814 | case 0 : { T = gsl_multifit_fdfsolver_lmsder; break; } |
815 | case 1 : { T = gsl_multifit_fdfsolver_lmder; break; } | ||
815 | default: ERROR(BAD_CODE); | 816 | default: ERROR(BAD_CODE); |
816 | } | 817 | } |
817 | 818 | ||
diff --git a/lib/Numeric/LinearAlgebra/Tests.hs b/lib/Numeric/LinearAlgebra/Tests.hs index c9408a6..60c60e6 100644 --- a/lib/Numeric/LinearAlgebra/Tests.hs +++ b/lib/Numeric/LinearAlgebra/Tests.hs | |||
@@ -143,19 +143,23 @@ odeTest = utest "ode" (last (toLists sol) ~~ [-1.7588880332411019, 8.36434890871 | |||
143 | 143 | ||
144 | --------------------------------------------------------------------- | 144 | --------------------------------------------------------------------- |
145 | 145 | ||
146 | fittingTest = utest "levmar" ok | 146 | fittingTest = utest "levmar" (ok1 && ok2) |
147 | where | 147 | where |
148 | xs = map return [0 .. 39] | 148 | xs = map return [0 .. 39] |
149 | sigma = 0.1 | 149 | sigma = 0.1 |
150 | ys = map return $ toList $ fromList (map (head . expModel [5,0.1,1]) xs) | 150 | ys = map return $ toList $ fromList (map (head . expModel [5,0.1,1]) xs) |
151 | + scalar sigma * (randomVector 0 Gaussian 40) | 151 | + scalar sigma * (randomVector 0 Gaussian 40) |
152 | dat = zipWith3 (,,) xs ys (repeat sigma) | 152 | dats = zip xs (zip ys (repeat sigma)) |
153 | dat = zip xs ys | ||
153 | 154 | ||
154 | expModel [a,lambda,b] [t] = [a * exp (-lambda * t) + b] | 155 | 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 | expModelDer [a,lambda,_b] [t] = [[exp (-lambda * t), -t * a * exp(-lambda*t) , 1]] |
156 | 157 | ||
157 | sol = fst $ fitModel 1E-4 1E-4 20 (resM expModel, resD expModelDer) dat [1,0,0] | 158 | sols = fst $ fitModelScaled 1E-4 1E-4 20 (expModel, expModelDer) dats [1,0,0] |
158 | ok = and (zipWith f sol [5,0.1,1]) where f (x,d) r = abs (x-r)<2*d | 159 | sol = fst $ fitModel 1E-4 1E-4 20 (expModel, expModelDer) dat [1,0,0] |
160 | |||
161 | ok1 = and (zipWith f sols [5,0.1,1]) where f (x,d) r = abs (x-r)<2*d | ||
162 | ok2 = pnorm PNorm2 (fromList (map fst sols) - fromList sol) < 1E-5 | ||
159 | 163 | ||
160 | --------------------------------------------------------------------- | 164 | --------------------------------------------------------------------- |
161 | 165 | ||