packages feed

monte-carlo-0.3: examples/Pi.lhs

In this example, we compute a Monte Carlo estimate of pi by
generating random points in the unit box, and counting how many
of them fall in the unit circle.

\begin{code}
import Control.Monad( liftM, liftM2 )
import Control.Monad.MC( MC, uniform, replicateMC, evalMC, mt19937 )
import Data.Summary.Bool( summary, sampleMean, sampleSE )
import Data.Summary.Utils( interval )
import Text.Printf
\end{code}

First, we need a function to test whether or not a point is in the
unit circle.  We define

\begin{code}
inUnitCircle :: (Double,Double) -> Bool
inUnitCircle (x,y) = x*x + y*y <= 1
\end{code}

The first line is the type signature, which tells us that "inUnitCircle"
is a function which takes a pair of `Double`s and returns a `Bool`.  In
English, "::" means "has type".  The second line is the one that defines
the function.

\begin{code}
estimatePi :: [(Double,Double)] -> (Double,Double)
estimatePi xs =
    let s       = summary $ map inUnitCircle xs
        (mu,se) = (sampleMean s, sampleSE s) in
    (4*mu,4*se)
\end{code}

Next, we need to generate a random point in the unit box.  In the
Control.Monad.MC module, there is a function for generating uniform
values in an interval, called "uniform".  This function has a 
funny-looking type, but you can think of it as:

    uniform :: Double -> Double -> MC Double

This type means that the function takes the two endpoints of the 
interval as arguments, and returns a Monte-Carlo action which produces
a Double.

You can think of the type "MC Double" as a random number generator.
For general types, "MC a" is a generator for values of type "a".  In
fact, "MC" is one of a general class of objects called a Monad.  We
use Monads in Haskell for making sure events happen in the right order.
That is, if we have three parts of a simulation, say A, B, and C, and
we want them to happen in the order
 
    A ====> B ====> C

then we would like to make sure that A is done consuming random numbers
before B consumes anything.  Likewise, we want B to finish before C
starts.  Monads are the magic that enable us to ensure this.

There are a number of functions in the standard library for working with
monads.  The first we will use is

    liftM2 :: (Monad m) => (a1 -> a2 -> r) -> m a1 -> m a2 -> m r

This function works on *any* Monad.  When we use it on the MC monad, it
will have type

    liftM2 :: (a1 -> a2 -> r) -> MC a1 -> MC a2 -> MC r
    
What liftM2 does is it takes a function of two arguments and two Monte
Carlo actions.  It returns a new Monte Carlo action that does the following:

  1. generate a random value of type a1 using the first action
  2. generate a random value of type a2 using the second action
  3. apply a function to the two values and return the result
  
We do not need to write liftM2 ourselves, since it is provided in the 
"Control.Monad" module.  But, if we did have to define it, the code would
look like:

liftM2 f ma1 ma2 = do
    a1 <- ma1
    a2 <- ma2
    return (f a1 a2)
    
This code for this uses the "do" notation of Haskell, which allows us
to specify a series of actions in sequential order.

\begin{code}
unitBox :: MC (Double,Double)
unitBox = liftM2 (,) (uniform (-1) 1) 
                     (uniform (-1) 1)
\end{code}

-- | Compute a Monte Carlo estimate of pi based on @n@ samples.  Return
-- the sample mean and standard error.

\begin{code}
simulation :: Int -> MC (Double,Double)
simulation n = 
    estimatePi `fmap` replicateMC n unitBox
\end{code}

\begin{code}
main =
    let seed    = 0
        n       = 1000000 
        (mu,se) = simulation n `evalMC` mt19937 seed
        (l,u)   = interval 0.95 mu se
    in do
        printf "\nEstimate: %g" mu
        printf "\n99%% Confidence Interval: (%g, %g)" l u
        printf "\n"
\end{code}