{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
module Data.DiGraph.Random
(
UniformRng
, rrgIO
, rrg
, gnp
) where
import Control.Monad
import Control.Monad.Trans.Class
import Control.Monad.Trans.Maybe
import qualified Data.Set as S
import Numeric.Natural
import qualified Streaming.Prelude as S
import qualified System.Random.MWC as MWC
import Data.DiGraph
type UniformRng m = (Int, Int) -> m Int
int :: Integral a => Num b => a -> b
int = fromIntegral
{-# INLINE int #-}
rrgIO
:: Natural
-> Natural
-> IO (Maybe (DiGraph Int))
rrgIO n d = MWC.withSystemRandom $ \gen -> rrg @IO (`MWC.uniformR` gen) n d
rrg
:: Monad m
=> UniformRng m
-> Natural
-> Natural
-> m (Maybe (DiGraph Int))
rrg gen n d = go 0 (S.fromList c) (emptyGraph n)
where
v = [0 .. int n - 1]
c = [(x, y) | x <- v, y <- [0 :: Int .. int d - 1]]
go i s g
| S.null s = return $ Just g
| (fst . fst <$> S.minView s) == (fst . fst <$> S.maxView s) = return Nothing
| otherwise = sampleEdge s g >>= \case
Nothing -> if i < n then go (i + 1) s g else return Nothing
Just (s', g') -> go 0 s' g'
sampleEdge s graph = runMaybeT $ do
(s', v₁) <- lift $ uniformSample gen s
(s'', v₂) <- lift $ uniformSample gen s'
let e₁ = (fst v₁, fst v₂)
let e₂ = (fst v₂, fst v₁)
guard $ fst v₁ /= fst v₂ && not (isEdge e₁ graph)
return (s'', insertEdge e₁ $ insertEdge e₂ graph)
uniformSample :: Monad m => UniformRng m -> S.Set a -> m (S.Set a, a)
uniformSample gen s = do
p <- gen (0, S.size s - 1)
return (S.deleteAt p s, S.elemAt p s)
gnp
:: forall m
. Monad m
=> UniformRng m
-> Natural
-> Double
-> m (DiGraph Int)
gnp gen n p = S.fold_ (flip insertEdge) (emptyGraph n) id
$ S.concat
$ S.filterM (const choice)
$ S.each
[ [(a,b), (b,a)]
| a <- [0..int n - 1]
, b <- [0..a-1]
]
where
choice = do
v <- gen (0, maxBound)
return $ int v <= p * int (maxBound :: Int)