monad-mersenne-random (empty) → 0.1
raw patch · 4 files changed
+280/−0 lines, 4 filesdep +basedep +mersenne-random-pure64setup-changed
Dependencies added: base, mersenne-random-pure64
Files
- Control/Monad/Mersenne/Random.hs +180/−0
- LICENSE +30/−0
- Setup.hs +3/−0
- monad-mersenne-random.cabal +67/−0
+ Control/Monad/Mersenne/Random.hs view
@@ -0,0 +1,180 @@+--------------------------------------------------------------------+-- |+-- Module : Control.Monad.Mersenne.Random+-- Copyright : (c) Don Stewart 2010+--+-- License : BSD3+--+-- Maintainer: Don Stewart <dons@galois.com>+-- Stability : provisional+--+-- A fast random number generator monad.+--++module Control.Monad.Mersenne.Random (++ -- * Random monad+ Rand(..),+ runRandom,+ evalRandom,++ -- * Efficient generators (Word64 is the primitive getter).+ getBool,+ getInt,+ getWord,+ getInt64,+ getWord64,+ getDouble,++ -- * Internals+ R(..),++ -- $example++ ) where++import Control.Monad+import Data.Word+import Data.Int+import System.Random.Mersenne.Pure64+++-- | The state of a random monad, optimized for performance.+data R a = R !a {-# UNPACK #-}!PureMT++------------------------------------------------------------------------++-- | A basic random monad, for generating random numbers from pure mersenne twisters.+newtype Rand a = Rand { runRand :: PureMT -> R a }++instance Monad Rand where++ {-# INLINE return #-}+ return a = Rand $ \s -> R a s++ {-# INLINE (>>=) #-}+ m >>= k = Rand $ \s -> case runRand m s of+ R a s' -> runRand (k a) s'++ {-# INLINE (>>) #-}+ m >> k = Rand $ \s -> case runRand m s of+ R _ s' -> runRand k s'++-- | Run a random computation using the generator @g@, returning the result+-- and the updated generator.+runRandom :: Rand a -> PureMT -> (a, PureMT)+runRandom r g = case runRand r g of R x g -> (x, g)++-- | Evaluate a random computation using the mersenne generator @g@. Note that the+-- generator @g@ is not returned, so there's no way to recover the+-- updated version of @g@.+evalRandom :: Rand a -> PureMT -> a+evalRandom r g = case runRand r g of R x _ -> x++------------------------------------------------------------------------+-- Efficient 'get' functions.++getBool :: Rand Bool+getBool = Rand $ \s -> case randomInt s of (w,s') -> R (w < 0) s'++-- | Yield a new 'Int' value from the generator.+getInt :: Rand Int+getInt = Rand $ \s -> case randomInt s of (w,s') -> R w s'++-- | Yield a new 'Word' value from the generator.+getWord :: Rand Word+getWord = Rand $ \s -> case randomWord s of (w,s') -> R w s'++-- | Yield a new 'Int64' value from the generator.+getInt64 :: Rand Int64+getInt64 = Rand $ \s -> case randomInt64 s of (w,s') -> R w s'++-- | Yield a new 53-bit precise 'Double' value from the generator.+getDouble :: Rand Double+getDouble = Rand $ \s -> case randomDouble s of (w,s') -> R w s'++-- | Yield a new 'Word64' value from the generator.+getWord64 :: Rand Word64+getWord64 = Rand $ \s -> case randomWord64 s of (w,s') -> R w s'++------------------------------------------------------------------------+-- $example+--+-- An example from a user on Stack Overflow -- taking a random walk, and+-- printing a histogram.+--+-- > {-# LANGUAGE BangPatterns #-}+-- >+-- > import System.Environment+-- > import Text.Printf+-- > import Control.Monad.Mersenne.Random+-- > import System.Random.Mersenne.Pure64+-- >+-- > main :: IO ()+-- > main = do+-- > (size:iters:_) <- fmap (map read) getArgs+-- > let start = take size $ repeat 0+-- > rnd <- newPureMT+-- > let end = flip evalRandom rnd $ mapM (iterateM iters randStep) start+-- > putStr . unlines $ histogram "%.2g" end 13+-- > +-- > {-# INLINE iterateM #-}+-- > iterateM n f x = go n x+-- > where+-- > go 0 !x = return x+-- > go n !x = f x >>= go (n-1)+-- > +-- > randStep :: Double -> Rand Double+-- > randStep x = do+-- > v <- getBool+-- > return $! if v then x+1 else x-1+-- > +-- >+-- > histogram :: String -> [Double] -> Int -> [String]+-- > histogram _ _ 0 = []+-- > histogram fmt xs bins =+-- > let xmin = minimum xs+-- > xmax = maximum xs+-- > bsize = (xmax - xmin) / (fromIntegral bins)+-- > bs = take bins $ zip [xmin,xmin+bsize..] [xmin+bsize,xmin+2*bsize..]+-- > counts :: [Int]+-- > counts = let cs = map count bs+-- > in (init cs) ++ [last cs + (length $ filter (==xmax) xs)]+-- > in map (format (maximum counts)) $ zip bs counts+-- > where+-- > toD :: (Real b) => b -> Double+-- > toD = fromRational . toRational+-- > count (xmin, xmax) = length $ filter (\x -> x >= xmin && x < xmax) xs+-- > format :: Int -> ((Double,Double), Int) -> String+-- > format maxc ((lo,hi), c) = +-- > let cscale = 50.0 / toD maxc+-- > hashes = take (round $ (toD c)*cscale) $ repeat '#'+-- > label = let los = printf fmt lo+-- > his = printf fmt hi+-- > l = los ++ " .. " ++ his+-- > pad = take (20 - (length l)) $ repeat ' '+-- > in pad ++ l+-- > in label ++ ": " ++ hashes+-- > +--+-- Compiling this:+--+-- > $ ghc -O2 --make B.hs+--+-- And running it:+--+-- > $ time E 300 5000+-- > -194.00 .. -164.46: +-- > -164.46 .. -134.92: #+-- > -134.92 .. -105.38: ####+-- > -105.38 .. -75.85: ###########+-- > -75.85 .. -46.31: #########################+-- > -46.31 .. -16.77: ##################################################+-- > -16.77 .. 12.77: #################################################+-- > 12.77 .. 42.31: ###########################################+-- > 42.31 .. 71.85: ###########################+-- > 71.85 .. 101.38: ################+-- > 101.38 .. 130.92: #######+-- > 130.92 .. 160.46: #####+-- > 160.46 .. 190.00: #+-- > ./E 500 3000 0.03s user 0.00s system 96% cpu 0.035 total
+ LICENSE view
@@ -0,0 +1,30 @@+Copyright Don Stewart 2010++All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions are met:++ * Redistributions of source code must retain the above copyright+ notice, this list of conditions and the following disclaimer.++ * Redistributions in binary form must reproduce the above+ copyright notice, this list of conditions and the following+ disclaimer in the documentation and/or other materials provided+ with the distribution.++ * Neither the name of Don Stewart nor the names of other+ contributors may be used to endorse or promote products derived+ from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ Setup.hs view
@@ -0,0 +1,3 @@+#!/usr/bin/env runhaskell+import Distribution.Simple+main = defaultMain
+ monad-mersenne-random.cabal view
@@ -0,0 +1,67 @@+-- monad-mersenne-random.cabal auto-generated by cabal init. For+-- additional options, see+-- http://www.haskell.org/cabal/release/cabal-latest/doc/users-guide/authors.html#pkg-descr.+-- The name of the package.+Name: monad-mersenne-random++-- The package version. See the Haskell package versioning policy+-- (http://www.haskell.org/haskellwiki/Package_versioning_policy) for+-- standards guiding when and how versions should be incremented.+Version: 0.1++-- A short (one-line) description of the package.+Synopsis: An efficient random generator monad, based on the Mersenne Twister++-- A longer description of the package.+Description: Often we need an efficient way to generate high+ quality pseudo-random numbers in Haskell. We have good generators+ themselves (for example, the mersenne-random-pure64 package), however, users+ are often tempted to store the generator in a lazy state monad. This+ causes performance problems.+ .+ This package provides an optimized 'Rand' monad for monadic generation+ of random numbers from a state, with close attention to performance. You+ may have results an order of magnitude or more better than using+ Control.Monad.State to store your generator.++-- URL for the project homepage or repository.+Homepage: http://code.haskell.org/~dons/code/monad-mersenne-random++-- The license under which the package is released.+License: BSD3++-- The file containing the license text.+License-file: LICENSE++-- The package author(s).+Author: Don Stewart++-- An email address to which users can send suggestions, bug reports,+-- and patches.+Maintainer: dons@galois.com++-- A copyright notice.+-- Copyright: ++-- Stability of the pakcage (experimental, provisional, stable...)+Stability: Provisional++Category: Control++Build-type: Simple++-- Extra files to be distributed with the package, such as examples or+-- a README.+-- Extra-source-files: ++-- Constraint on the version of Cabal needed to build this package.+Cabal-version: >=1.2+++Library+ -- Modules exported by the library.+ Exposed-modules: Control.Monad.Mersenne.Random+ + -- Packages needed in order to build this package.+ Build-depends: base > 3 && < 6, mersenne-random-pure64+