Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Sampling a normal distribution using Control.Monad.MonadRandom

I want to sample a Normal distribution with given mean and standard deviation. I know how to do that in various contexts like Data.Random.Rvar or Data.Random.MonadRandom. However, the context of my function is Control.Monad.MonadRandom, and I would like to keep it that way as my whole project uses Control.Monad.MonadRandom.

Is there any way to do so, and could you help me do that?

Here is how the code looks like. Pattern is just an alias for Data.Vector Double and Weights is an alias for Data.Vector (Data.Vector Double) (ie a matrix)

train :: MonadRandom m => [Pattern] -> Int -> m Weights
train pats nr_hidden = do
  ws_start <- ws_start''
  foldM updateWS ws_start pats
    where ws_start'  = take p (repeat $ take nr_hidden $ repeat $ (normal 0.0 0.01))
         ws_start'' = vector2D <$> (sequence $ map sequence ws_start')
         p = length pats

Thank you.

like image 770
elaRosca Avatar asked Dec 18 '12 15:12

elaRosca


1 Answers

The quick answer

How to use a Data.Random.RVar inside a Control.Monad.MonadRandom?

{-# LANGUAGE ScopedTypeVariables #-}

import Control.Monad.Random as CMR
import Data.Random          as DR
import Data.Word (Word32)

gimmeRandom :: forall m . CMR.MonadRandom m => m Int
gimmeRandom = do
  r <- runRVar (uniform 0 100) (getRandom :: m Word32)
  return r

Explanation

Effectively, you want to run a Monad inside a formally different Monad with similar semantics.

  • Data.Random.MonadRandom and Control.Monad.Random are formally different because they are defined indepently in different places, and none is an instance of the other (there is no instance DR.MonadRandom m => CMR.MonadRandom m or the other way around).
  • The Monads have similar semantics because they both provide random numbers from some randomness source, so it makes sense to expect that we can combine them somehow.

Let us say you have some code in a Control.Monad.Random interface:

import Control.Monad.Random as CMR

gimmeRandom :: CMR.MonadRandom m => m Int
gimmeRandom = do
  r <- getRandomR (0, 100)
  return r

We could run this like evalRand gimmeRandom StdGen which gives us an Int.

Now instead of getRandomR, you want to use one of the many available distributions provided by Data.Random.

For this example, we will try to replace getRandomR (0, 100) by uniform 0 100 :: RVar Int. How to we get the Int out of that RVar Int in our CMR.MonadRandom environment?

We want to run the RVar monad, for which we will probably have to provide a random number source, as the semantic suggests. We are looking for a monad-escaping function like evalRand for CMR. These escaping functions have type m a -> someStuffNeededToRunTheMonad -> a.

In the docs about RVar, there is an example:

-- In a monad, using a RandomSource:
runRVar (uniform 1 100) DevRandom :: IO Int

Let's check runRVar:

runRVar :: RandomSource m s => RVar a -> s -> m a

Yes, that is a kind of escaping function: Given an RVar and a source for random numbers, it returns us the random result of the RVar inside our own monad m. This, however, requires, that there is an instance RandomSource m s that says that s is a randomness source for our monad m. Let's look for that instance.

What is our monad m? We want to run the RVar in gimmeRandom, so the monad is CMR.MonadRandom m => m (all monads that implement CMR.MonadRandom). What is the randomness source s? No clue yet. Let us look in the docs which RandomSource instances exist:

RandomSource IO DevRandom
...
Monad m0 => RandomSource m0 (m0 Word32)
Monad m0 => RandomSource m0 (m0 Word64)
...

Aha! This says that any monad m0 is an instance of RandomSource together with a value coming from this monad (e.g. m0 Word32). This hold of course also for our monad CMR.MonadRandom. We can also see that the s, m0 Word32, must be the random value generated by the randomness source.

What should we pass in as the s in runRVar (uniform 0 100) s? Something that generates random numbers in our monad, something of type CMR.MonadRandom m => m Word32. What is the CMR function to generate arbitrary things, e.g. some Word32? getRandom. So basically we want to write:

gimmeRandom :: CMR.MonadRandom m => m Int
gimmeRandom = do
  r <- runRVar (uniform 0 100) getRandom
  return r

Hmm, that does not compile:

Could not deduce (RandomSource m (m0 a0))
  arising from a use of `runRVar'
from the context (CMR.MonadRandom m)
  bound by the type signature for
             gimmeRandom :: CMR.MonadRandom m => m Int

RandomSource m (m0 a0)? That is weird, the m and the m0 seem to be recognised as different monads by the compiler; we want them to be the same, as in RandomSource m0 (m0 Word64).

Let us put the full signature into that place:

r <- runRVar (uniform 0 100) (getRandom :: CMR.MonadRandom m => m Word32)

Still the same error. This is because the m in that type signature, is really any monad implementing CMR.MonadRandom, not necessarily the MonadRandom in our gimmeRandom type signature.

(This is the same concept of shadowing as in lambda terms (\x -> (\x -> f x)) where the inner \x is the one used in f x; or in first-order logic like ∀x . F(x) → ∀x . G(x), where the x in G(x) is the innermost defined one and need not be the same, not even of the same type, as the one in the outer ∀x; or really in any other programming language with variable hiding/shadowing in inner scopes - just that here it is type variable shadowing).

So the only thing that we have to do is to tell the compiler that in the getRandom invocation, we don't want that to be for any MonadRandom, but for exactly that MonadRandom m that we have in the gimmeRandom type signature.

We can do that using the ScopedTypeVariables extension:

{-# LANGUAGE ScopedTypeVariables #-}

[...]

gimmeRandom :: forall m . CMR.MonadRandom m => m Int
gimmeRandom = do
  r <- runRVar (uniform 0 100) (getRandom :: m Word32)
  return r

This makes that the m in getRandom :: m ... is to be chosen exactly that CMR.MonadRandom m from the top-level type signature.

This does compile and the problem is solved: We can use distributions from Data.Random in code using the MonadRandom interface. We could easily replace the uniform by another distribution.

To summarise, we have

  • identified that we use two different monads from different packages but with same/overlapping semantics
  • found out how to run/escape the monad we want to use inside our own (with runRVar)
  • found out what to pass into the escaping function by looking at its typeclass restrictions and the instances provided for those
  • written the right code (runRVar (uniform 0 100) getRandom)
  • made it compile by saying which precise monad getRandom shall use.

If you are wondering why we chose Word32 somewhat arbitrarily from the instances we can pick from, we just have to give the randomness source in some form, and Word32 is one of the things Data.Random takes as input for generating other random stuff.

like image 54
nh2 Avatar answered Nov 15 '22 23:11

nh2