module Main where import qualified Numeric.Interpolation.NodeList as Nodes import qualified Numeric.Interpolation.Piecewise as Piecewise import qualified Numeric.Interpolation.Type as Type import qualified Data.Packed.ST as PackST import qualified Data.Packed.Matrix as Matrix import qualified Data.Packed.Vector as Vector import Data.Packed.Matrix (Matrix) import Data.Packed.Vector (Vector) import qualified Numeric.LinearAlgebra.Banded as Banded import qualified Numeric.Container as Container import Numeric.Container ((<\>)) import qualified Graphics.Gnuplot.Advanced as GP import qualified Graphics.Gnuplot.Plot.TwoDimensional as Plot2D import qualified Graphics.Gnuplot.Graph.TwoDimensional as Graph2D import System.Random (randomRs, mkStdGen) import Control.Monad.HT (void) import Control.Monad (when, zipWithM_, forM_) import qualified Data.Foldable as Fold import Data.Monoid ((<>)) noisy :: [(Double, Double)] noisy = take 100 $ zipWith (\x d -> (x, sin x + d)) (randomRs (0,7) (mkStdGen 23)) (randomRs (-0.2,0.2) (mkStdGen 42)) basisMatrixFull :: Type.T Double Double ny -> [Double] -> [Double] -> Matrix Double basisMatrixFull typ xs txs0 = let txs = Vector.fromList txs0 in Matrix.fromColumns $ map (flip Container.cmap txs . Piecewise.interpolateConstantExt typ) $ Type.basisFunctions typ xs basisMatrixSparse :: Type.T Double Double ny -> [Double] -> [Double] -> Matrix Double basisMatrixSparse typ xs txs = PackST.runSTMatrix $ do mat <- PackST.newMatrix 0 (length txs) (length $ Type.basisFunctions typ xs) zipWithM_ (\k -> mapM_ (uncurry (PackST.writeMatrix mat k))) [0..] $ map (Type.sampleBasisFunctions typ xs) txs return mat fit :: Type.T Double Double ny -> [Double] -> [(Double, Double)] -> Nodes.T Double ny fit typ xs target = let (txs, tys) = unzip target matrix = basisMatrixSparse typ xs txs in Type.coefficientsToInterpolator typ xs $ Vector.toList $ matrix <\> Vector.fromList tys matrixDiff :: Type.T Double Double ny -> [Double] -> [(Double, Double)] -> Double matrixDiff typ xs target = let txs = map fst target in Container.maxElement $ Container.cmap abs $ Container.sub (basisMatrixFull typ xs txs) (basisMatrixSparse typ xs txs) mulSparseMatrixVector :: Int -> [[(Int, Double)]] -> [Double] -> Vector Double mulSparseMatrixVector size samples tys = PackST.runSTVector $ do vec <- PackST.newVector 0 size forM_ (zip samples tys) $ \(row,ty) -> forM_ row $ \(k,y) -> PackST.modifyVector vec k (+y*ty) return vec bandedGramian :: Int -> Int -> [[(Int, Double)]] -> Banded.SymmetricMatrix Double bandedGramian size width samples = Banded.SymmetricMatrix $ PackST.runSTMatrix $ do mat <- PackST.newMatrix 0 size width forM_ samples $ \row -> forM_ row $ \(k,yk) -> forM_ row $ \(j,yj) -> when (k<=j) $ PackST.modifyMatrix mat k (j-k) (+yk*yj) return mat fitBanded :: Type.T Double Double ny -> [Double] -> [(Double, Double)] -> Nodes.T Double ny fitBanded typ xs target = let size = length $ Type.basisFunctions typ xs (txs, tys) = unzip target samples = map (Type.sampleBasisFunctions typ xs) txs matrix = Banded.choleskyDecompose $ bandedGramian size (Type.basisOverlap typ) samples in Type.coefficientsToInterpolator typ xs $ Vector.toList $ Banded.choleskySolve matrix $ mulSparseMatrixVector size samples tys bandedDiff :: (ny -> ny -> Double) -> Type.T Double Double ny -> [Double] -> [(Double, Double)] -> Double bandedDiff absDiff typ xs target = maximum $ zipWith absDiff (Fold.toList $ fit typ xs target) (Fold.toList $ fitBanded typ xs target) absDiffSingle :: Double -> Double -> Double absDiffSingle x y = abs (x-y) absDiffPair :: (Double,Double) -> (Double,Double) -> Double absDiffPair (x,dx) (y,dy) = max (abs (x-y)) (abs (dx-dy)) plotBasisFunctions :: Type.T Double Double ny -> [Double] -> Plot2D.T Double Double plotBasisFunctions nodeType xs = let abscissa = Plot2D.linearScale 1000 (minimum xs, maximum xs) in Plot2D.functions Graph2D.lines abscissa $ map (Piecewise.interpolateConstantExt nodeType) $ Type.basisFunctions nodeType xs main :: IO () main = do let xs = [0, 1, 3, 4, 6, 7] exs = (-1) : xs ++ [8] void $ GP.plotDefault $ plotBasisFunctions Type.linear xs void $ GP.plotDefault $ plotBasisFunctions Type.hermite1 xs void $ GP.plotDefault $ plotBasisFunctions Type.cubicLinear exs void $ GP.plotDefault $ plotBasisFunctions Type.cubicParabola exs let linearNodes = fit Type.linear xs noisy hermite1Nodes = fit Type.hermite1 xs noisy cubicLinearNodes = fit Type.cubicLinear exs noisy cubicParabolaNodes = fit Type.cubicParabola exs noisy void $ GP.plotDefault $ Plot2D.list Graph2D.points noisy <> (Plot2D.functions Graph2D.lines (Plot2D.linearScale 1000 (-2,10)) $ Piecewise.interpolateConstantExt Type.linear linearNodes : Piecewise.interpolateConstantExt Type.hermite1 hermite1Nodes : Piecewise.interpolateConstantExt Type.cubicLinear cubicLinearNodes : Piecewise.interpolateConstantExt Type.cubicParabola cubicParabolaNodes : []) putStrLn "differences between matrices should be almost zero:" putStrLn $ "linear: " ++ show (matrixDiff Type.linear xs noisy) putStrLn $ "hermite1: " ++ show (matrixDiff Type.hermite1 xs noisy) putStrLn $ "cubicLinear: " ++ show (matrixDiff Type.cubicLinear exs noisy) putStrLn $ "cubicParabola: " ++ show (matrixDiff Type.cubicParabola exs noisy) putStrLn "differences between samples should be almost zero:" putStrLn $ "linear: " ++ show (bandedDiff absDiffSingle Type.linear xs noisy) putStrLn $ "hermite1: " ++ show (bandedDiff absDiffPair Type.hermite1 xs noisy) putStrLn $ "cubicLinear: " ++ show (bandedDiff absDiffPair Type.cubicLinear exs noisy) putStrLn $ "cubicParabola: " ++ show (bandedDiff absDiffPair Type.cubicParabola exs noisy)