{-# LANGUAGE Rank2Types #-} module Main where import qualified Plot2DExtra import qualified Numeric.Interpolation.NodeList as Nodes import qualified Numeric.Interpolation.Piecewise as Piecewise import qualified Numeric.Interpolation.Type as Type import qualified Numeric.LAPACK.Singular as Singular import qualified Numeric.LAPACK.ShapeStatic as ShapeStatic import qualified Numeric.LAPACK.Matrix.Shape as MatrixShape import qualified Numeric.LAPACK.Matrix.BandedHermitianPositiveDefinite as BandedSPD import qualified Numeric.LAPACK.Matrix.BandedHermitian as BandedHermitian import qualified Numeric.LAPACK.Matrix.Array as ArrMatrix import qualified Numeric.LAPACK.Matrix as Matrix import qualified Numeric.LAPACK.Vector as Vector import qualified Type.Data.Num.Unary as Unary import Type.Base.Proxy (Proxy) import qualified Data.Array.Comfort.Storable as Array import qualified Data.Array.Comfort.Shape as Shape import qualified Graphics.Gnuplot.Advanced as GP import qualified Graphics.Gnuplot.Frame as Frame import qualified Graphics.Gnuplot.Frame.OptionSet as Opts import qualified Graphics.Gnuplot.Plot.TwoDimensional as Plot2D import qualified Graphics.Gnuplot.Graph.TwoDimensional as Graph2D import qualified Graphics.Gnuplot.LineSpecification as LineSpec import System.Random (randomRs, mkStdGen) import Control.Monad.HT (void) import Control.Applicative ((<$>)) import qualified Data.Foldable as Fold import Data.Tuple.HT (mapFst) import Data.Monoid ((<>)) type BandedHermitianMatrix k = BandedHermitian.BandedHermitian k Matrix.ShapeInt type Matrix = Matrix.General Matrix.ShapeInt Matrix.ShapeInt type Vector = Vector.Vector Matrix.ShapeInt type ShortVector k = Vector.Vector (ShapeStatic.ZeroBased k) 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.autoFromList txs0 in Matrix.fromColumns (Array.shape txs) $ map (flip Array.map txs . Piecewise.interpolateConstantExt typ) $ Type.basisFunctions typ xs zipRowsWith :: (a -> b -> c) -> [a] -> [[b]] -> [c] zipRowsWith f as bs = concat $ zipWith (map . f) as bs -- ToDo: generalize shapes basisMatrixSparse :: Type.T Double Double ny -> [Double] -> [Double] -> Matrix Double basisMatrixSparse typ xs txs = Matrix.fromRowMajor $ Array.fromAssociations 0 (Shape.ZeroBased $ length txs, Shape.ZeroBased $ length $ Type.basisFunctions typ xs) $ zipRowsWith (\k (j,x) -> ((k,j),x)) [0..] $ map (Type.sampleBasisFunctions typ xs) txs 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.unliftColumn MatrixShape.ColumnMajor (snd . Singular.leastSquaresMinimumNormRCond 1e-5 matrix) $ Vector.autoFromList tys matrixDiff :: Type.T Double Double ny -> [Double] -> [(Double, Double)] -> Double matrixDiff typ xs target = let txs = map fst target in Vector.normInf $ ArrMatrix.toVector $ Matrix.sub (basisMatrixFull typ xs txs) (basisMatrixSparse typ xs txs) mulSparseMatrixVector :: Int -> [[(Int, Double)]] -> [Double] -> Vector Double mulSparseMatrixVector size samples tys = Array.accumulate (+) (Vector.zero (Matrix.shapeInt size)) (zipRowsWith (\ty (k,y) -> (k, y*ty)) tys samples) {- ToDo: use index type (ZeroInt, Enumeration Order) with Order = Absolute | Derivative Problem: not all interpolation types use derivatives -} shortVector :: (Unary.Natural k) => Proxy k -> [(Int, Double)] -> (Int, ShortVector k Double) shortVector width xs = let i0 = minimum $ map fst xs in (i0, -- Array.fromAssociations 0 Shape.static $ Array.reshape Shape.static $ Array.fromAssociations 0 (Matrix.shapeInt $ Unary.integralFromProxy width) $ map (mapFst (subtract i0)) xs) bandedGramian :: (Unary.Natural k) => Int -> Proxy (Unary.Succ k) -> [[(Int, Double)]] -> BandedHermitianMatrix k Double bandedGramian size width samples = BandedHermitian.sumRank1 MatrixShape.ColumnMajor (Matrix.shapeInt size) $ map ((,) 1 . shortVector width) samples reifyPositive :: Integer -> (forall s. Unary.Natural s => Proxy (Unary.Succ s) -> w) -> w reifyPositive n f = Unary.reifyNatural (n-1) (f . Unary.succ) 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 in reifyPositive (toInteger $ Type.basisOverlap typ) (\width -> Type.coefficientsToInterpolator typ xs $ Vector.toList $ Matrix.unliftColumn MatrixShape.ColumnMajor (BandedSPD.solve (bandedGramian size width samples)) $ 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 :: (String, Type.T Double Double ny) -> [Double] -> Frame.T (Graph2D.T Double Double) plotBasisFunctions (title, nodeType) xs = let abscissa = Plot2D.linearScale 1000 (minimum xs, maximum xs) in Frame.cons (Opts.title title $ Opts.key False Opts.deflt) $ Plot2D.functions Graph2D.lines abscissa $ map (Piecewise.interpolateConstantExt nodeType) $ Type.basisFunctions nodeType xs typeLinear :: (String, Type.T Double Double Double) typeLinear = ("linear", Type.linear) typeHermite1, typeCubicLinear, typeCubicParabola :: (String, Type.T Double Double (Double, Double)) typeHermite1 = ("hermite1", Type.hermite1) typeCubicLinear = ("cubicLinear", Type.cubicLinear) typeCubicParabola = ("cubicParabola", Type.cubicParabola) main :: IO () main = do let xs = [0, 1, 3, 4, 6, 7] exs = (-1) : xs ++ [8] void $ GP.plotDefault $ plotBasisFunctions typeLinear xs void $ GP.plotDefault $ plotBasisFunctions typeHermite1 xs void $ GP.plotDefault $ plotBasisFunctions typeCubicLinear exs void $ GP.plotDefault $ plotBasisFunctions typeCubicParabola 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 $ (Graph2D.lineSpec (LineSpec.title "target" LineSpec.deflt) <$> Plot2D.list Graph2D.points noisy) <> (let interpolate (name,typ) nodes = (name, Piecewise.interpolateConstantExt typ nodes) in Plot2DExtra.functions (Plot2D.linearScale 1000 (-2,10)) $ interpolate typeLinear linearNodes : interpolate typeHermite1 hermite1Nodes : interpolate typeCubicLinear cubicLinearNodes : interpolate typeCubicParabola cubicParabolaNodes : []) putStrLn "differences between matrices should be almost zero:" let printMatrixDiff (name,typ) ps = putStrLn $ name ++ ": " ++ show (matrixDiff typ ps noisy) printMatrixDiff typeLinear xs printMatrixDiff typeHermite1 xs printMatrixDiff typeCubicLinear exs printMatrixDiff typeCubicParabola exs putStrLn "differences between samples should be almost zero:" let printBandedDiff diff (name,typ) ps = putStrLn $ name ++ ": " ++ show (bandedDiff diff typ ps noisy) printBandedDiff absDiffSingle typeLinear xs printBandedDiff absDiffPair typeHermite1 xs printBandedDiff absDiffPair typeCubicLinear exs printBandedDiff absDiffPair typeCubicParabola exs