module Boltzmann.Data.Data where
import Control.Arrow ( (&&&) )
import Control.Applicative
import Data.Data
import Data.Foldable
import Data.Maybe
import qualified Data.HashMap.Lazy as HashMap
import Boltzmann.Data.Oracle
import Boltzmann.Data.Types
data SG r = SG
{ minSize :: Size
, maxSizeM :: Maybe Size
, runSG :: Points -> Maybe Double -> r
, runSmallG :: Points -> r
} deriving Functor
type Points = Int
rangeSG :: SG r -> (Size, Maybe Size)
rangeSG = minSize &&& maxSizeM
applySG :: SG r -> Points -> Maybe Double -> r
applySG SG{..} k sizeM
| Just minSize == maxSizeM = runSG k (fmap fromIntegral maxSizeM)
| Just size <- sizeM, size <= fromIntegral minSize =
error "Target size too small."
| Just True <- liftA2 ((<=) . fromIntegral) maxSizeM sizeM =
error "Target size too large."
| Nothing <- sizeM, Just _ <- maxSizeM =
error "Cannot make singular sampler for finite type."
| otherwise = runSG k sizeM
make :: (Data a, MonadRandomLike m)
=> [Alias m] -> proxy a -> SG (m a)
make aliases a =
SG minSize maxSizeM make' makeSmall
where
dd = collectTypes aliases a
t = typeRep a
i = case index dd #! t of
Left j -> fst (xedni' dd #! j)
Right i -> i
minSize = natToInt $ fst (lTerm dd #! i)
maxSizeM = HashMap.lookup i (degree dd)
make' k sizeM = getGenerator dd' generators a k
where
dd' = dds !! k
oracle = makeOracle dd' t sizeM
generators = makeGenerators dd' oracle
makeSmall k = getSmallGenerator dd' (smallGenerators dd') a
where
dd' = dds !! k
dds = iterate point dd
makeR :: (Data a, MonadRandomLike m)
=> [AliasR m] -> proxy a
-> SG ((Size, Size) -> m a)
makeR aliases a = fmap (flip runRejectT) (make aliases a)
type Size' = Int
rescale :: SG r -> Size' -> Double
rescale (SG minSize (Just maxSize) _ _) size' =
fromIntegral minSize + fromIntegral (min 99 size' * (maxSize minSize)) / 100
rescale (SG minSize Nothing _ _) size' = fromIntegral (minSize + size')
apply :: SG r -> Points -> Maybe Size' -> r
apply sg k (Just 0) = runSmallG sg k
apply sg k size' = runSG sg k (fmap (rescale sg) size')
applyR :: SG ((Size, Size) -> r) -> Points -> Maybe Size' -> (Size', Size') -> r
applyR sg k size' = apply sg k size' . rescaleInterval sg
rescaleInterval :: SG r -> (Size', Size') -> (Size, Size)
rescaleInterval sg (a', b') = (a, b)
where
a = (clamp . floor .rescale sg) a'
b = (clamp . ceiling . rescale sg) b'
clamp x
| Just maxSize <- maxSizeM sg, x >= 100 = maxSize
| otherwise = x
epsilon :: Double
epsilon = 0.1
tolerance :: Double -> Int -> (Int, Int)
tolerance epsilon size = (size delta, size + delta)
where
delta = ceiling (fromIntegral size * epsilon)
memo
:: (t -> [t2] -> SG r)
-> (SG r -> t1 -> Maybe Int -> a)
-> t -> t1 -> Int -> a
memo make apply aliases k = generators
where
sg = make aliases []
generators = sparseSized (apply sg k . Just) (99 <$ maxSizeM sg)
sparseSized :: (Int -> a) -> Maybe Int -> Int -> a
sparseSized f maxSizeM =
maybe a0 snd . \size' -> find ((>= size') . fst) as
where
as = [ (s, f s) | s <- ss ]
ss = 0 : maybe id (takeWhile . (>)) maxSizeM [ 2 ^ e | e <- [ 0 :: Int ..] ]
a0 = f (fromJust maxSizeM)