Skip to content
Browse files

More comments and haddock

  • Loading branch information...
1 parent c3be761 commit 22fee865f0711de2355b2bbeecf344edd8371aaf @bjpop committed Aug 28, 2011
Showing with 39 additions and 18 deletions.
  1. +39 −18 src/Random/LFG.hs
View
57 src/Random/LFG.hs
@@ -76,66 +76,81 @@ step _ = error $ "generator was empty"
generators :: [Gen]
generators = map (Gen . lfg) $ chunks initK Init.randomSequence
-type Index = Int -- unsafe read/write require Int arguments
+-- | indices into the lag table, unsafe read/write require Int arguments
+type Index = Int
+-- | the lag table, keeping the last K values in the sequence
+-- STUArray is faster than STArray.
type LagTable s = STUArray s Index Element
+-- | hard coded values for the lags J and K, these must be carefully chosen
initJ, initK :: Index
initJ = 861
initK = 1279
--- STUArray is faster than STArray.
-- strictness annotations in Gen do make appreciable difference to time/space,
-- especially with unpack pragmas.
-
data GenST s =
GenST
- { lagTable :: LagTable s -- the last k values in the sequence
- , j :: {-# UNPACK #-} !Index -- the position of the smaller lag
- , k :: {-# UNPACK #-} !Index -- the position of the larger lag
- , pos :: {-# UNPACK #-} !Index -- the position of the next element in the sequence
- , lagSize :: Index -- size of the lag table, must be equal to k
+ { lagTable :: LagTable s -- ^ the last K values in the sequence
+ , j :: {-# UNPACK #-} !Index -- ^ the position of the smaller lag
+ , k :: {-# UNPACK #-} !Index -- ^ the position of the larger lag
+ , pos :: {-# UNPACK #-} !Index -- ^ the position of the next element in the sequence
+ , lagSize :: Index -- ^ size of the lag table, must be equal to K
}
+-- | An exception type to indicate that the generator was not initialised correctly,
+-- namely that the value of J was >= K.
data LFGException = InitLagException
deriving (Show, Typeable)
instance Exception LFGException
--- initialise a RNG using the small lag, large lag and list of
+-- | Initialise a LFG using the small lag, large lag and a finite list of
-- seed elements (these should be "random"), ie use another random
--- number generator to make them.
--- init :: Index -> Index -> [Element] -> S.ST s (Gen s)
+-- number generator to make them. The input list of random numbers must
+-- have length >= K, the size of the lag table.
initST :: Index -> Index -> [Element] -> S.ST s (GenST s)
initST j k is = do
+ -- check that the lags are appropriate
when (j >= k) $ throw InitLagException
+ -- check that enough initial values have been provided
+ when (length is < k) $ throw InitLagException
+ -- initialise a new lag table from the input elements
array <- newListArray (0, k-1) is
+ -- return the initial state of the generator
return $ GenST { lagTable = array
- , j = j-1
- , k = k-1
+ , j = j-1 -- indices are zero-based
+ , k = k-1 -- indices are zero-based
, lagSize = k
- , pos = 0
+ , pos = 0 -- current position is at the start of the table
}
--- an explicit INLINE pragma on step makes a difference when step is exported.
--- unsafe read and write drop a non-trivial amount of time, between 1-10%
-
+-- | Advance the generator by one step, yielding the next value in the sequence
+-- and a new generator state.
{-# INLINE stepST #-}
+-- an explicit INLINE pragma on step makes a difference when step is exported.
stepST :: GenST s -> S.ST s (Element, GenST s)
stepST gen@(GenST { lagTable = array, j = oldJ, k = oldK, lagSize = size, pos = currentPos }) = do
+ -- the "unsafe" reads are actually safe because we know they will be in bounds of the lag table.
+ -- unsafe read and write drop a non-trivial amount of time, between 1-10%
jth <- unsafeRead array oldJ
kth <- unsafeRead array oldK
- -- x_i = (x_j + x_k) mod 1
+ -- compute the next element from: x_i = (x_j + x_k) mod 1
-- is there a haskell equivalent to fmod?
let (_, nextElement) = properFraction (jth + kth) :: (Integer, Double)
+ -- overwrite the last value in the table with the new element
unsafeWrite array currentPos nextElement
+ -- advance the lag indices and wrap around if necessary
let newJ = nextIndex oldJ
newK = nextIndex oldK
newPos = nextIndex currentPos
return (nextElement, gen { pos = newPos, j = newJ, k = newK })
where
+ -- increment an index and wrap to zero if we reach the end of the table
nextIndex :: Index -> Index
nextIndex i = if i == 0 then size - 1 else i - 1
+-- | Run a generator to produce a list of elements lazily.
genRands :: GenST s -> L.ST s [Element]
genRands gen = do
(next, newGen) <- strictToLazyST $ stepST gen
@@ -144,11 +159,17 @@ genRands gen = do
-- may get thunk-leak otherwise
return (next : ys)
+-- | Given a finite list of initial random values, rturn a ST computation yielding a
+-- infinite sequence of random numbers. NOTE: the input sequence of
+-- random numbers must have length at least K (the size of the lag table).
lfgST :: [Element] -> L.ST s [Element]
lfgST is = do
+ -- initialise the generator
gen <- strictToLazyST $ initST initJ initK is
+ -- start producing the sequence
genRands gen
+-- | A pure interface to the LFG generator, given an initial sequence of random numbers.
lfg :: [Element] -> [Element]
lfg seed = L.runST $ lfgST seed

0 comments on commit 22fee86

Please sign in to comment.
Something went wrong with that request. Please try again.