packages feed

Rlang-QQ-0.1.0.0: examples/romberg.hs

{-# LANGUAGE QuasiQuotes #-}

-- | example comparing the convergence of <http://en.wikipedia.org/wiki/Romberg's_method romberg integration>
-- in terms of the built-in error estimate as well as an \"exact\" solution ("Data.Number.CReal")
--
-- originally submitted to uwaterloo's CHE 322. Adapted plot to be done
-- with R.
module Main where

import Data.Number.CReal
import Data.List
import Data.Typeable
import RlangQQ

import Data.HList.CommonMain
import Control.Monad.State
import Data.Int


main = do
      mkPlt 22 (1 :: Double)
      mkPlt 22 (1 :: Float)


-- number of traces on the plots
-- ie. 1 = trapezoid only, 2 = trapezoid + simpsons
-- 3 = trapezoid + simpsons + simpsons 3/8
-- 4 = ...
nTraces = [n| 5 |]

mkPlt nMax proxy =
    let exact = exp 10 - 1
        calc = rombM exp 0 10


        xs = listToRecN nTraces $ map (take nMax . map (\x -> toDouble $
                            abs (tfr x - exact) / exact))
           (calc `asTypeOf` [[proxy]])
        es = listToRecN nTraces $ (map (take nMax . map tfr) $ getRombErr calc :: [[Double]])

        outputfile = "romberg_" ++ show (typeOf proxy) ++ ".pdf"

        ls = [1 .. hNat2Integral nTraces :: Int32]
        ns = [1 .. fromIntegral nMax     :: Double]

    in
 [r|

        library(lattice); library(reshape); library(plyr);

        conv <- function(hsvar) { # should be a better way to get a data.frame
            x <- data.frame(hsvar)
            names(x) <- hs_ls
            x <- ddply(melt(x), .(variable), function(x)
                    data.frame(x, refinements = hs_ns   ))
            within(x, refinements <- as.integer(variable) + refinements)
        }

        es <- conv(hs_es)
        xs <- conv(hs_xs)

        pdf(hs_outputfile)

        plot(xyplot( value ~ refinements, xs, groups = variable,
                scales= list(y=list(log=T),x=list(log=T)), type='l',
                ylab='error', auto.key=list(title='order', type='l',
                                            columns= hs_ls[length(hs_ls)] ),
                main = hs_outputfile))

        plot(xyplot( xs$value ~ es$value, groups=xs$variable, type='b',
                scales=list(x=list(log=T), y=list(log=T)),
                ylab='actual error',
                xlab='estimated error',
                panel= function(...){
                    panel.xyplot(...)
                    panel.abline(a=0, b=1, col='black')
                }))

        dev.off()

    |]



-- | trapezoid rule. doesn't reuse previous values
trap f a b n =
    let h = (b - a) / fromIntegral n
        xs = take (n-1) $ iterate (h+) (a+h)
            -- easier to see as [a+h, a+2*h .. b-h])
            -- but some interval arithmetic doesn't support [a..b]
    in h * (foldl' (\s x -> s + f x) ((f a + f b) / 2) xs)

rombM f a b =
    let is = map (\n -> trap f a b (2^n)) [0..]
    in map snd
        $ iterate
            (\(n,s) -> (n+1, zipWith (improve n) (tail s) s))
            (2,is)

romb f a b = map head $ rombM f a b

improve n x y = let c = 4^(n-1) in (c*x - y) / (c - 1)

getRombErr a = unStripe . map (\x -> zipWith (\a b -> abs (a - b)) x (tail x))
                $ stripe a

-- shorthand conversions
tfr x = fromRational (toRational x)
toDouble x = read (showCReal 100 x) :: Double


-- succesful with infinite stripes only
unStripe ss = unfoldr (\xs -> case unzip (map (splitAt 1) xs) of
                        (a,b) -> Just (concat a, b))
                    ss

-- stolen from Control.Monad.Omega
stripe [] = []; stripe ([]:xss) = stripe xss
stripe ((x:xs):xss) = [x] : zipCons xs (stripe xss)

zipCons [] ys = ys; zipCons xs [] = map (:[]) xs
zipCons (x:xs) (y:ys) = (x:y) : zipCons xs ys