packages feed

generic-random (empty) → 0.1.0.0

raw patch · 11 files changed

+1514/−0 lines, 11 filesdep +MonadRandomdep +QuickCheckdep +adsetup-changed

Dependencies added: MonadRandom, QuickCheck, ad, base, containers, criterion, deepseq, generic-random, hashable, hmatrix, ieee754, mtl, transformers, unordered-containers, vector

Files

+ LICENSE view
@@ -0,0 +1,22 @@+The MIT License (MIT)++Copyright (c) 2016 Li-yao Xia++Permission is hereby granted, free of charge, to any person obtaining a copy+of this software and associated documentation files (the "Software"), to deal+in the Software without restriction, including without limitation the rights+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell+copies of the Software, and to permit persons to whom the Software is+furnished to do so, subject to the following conditions:++The above copyright notice and this permission notice shall be included in all+copies or substantial portions of the Software.++THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE+SOFTWARE.+
+ README.md view
@@ -0,0 +1,41 @@+Generic random generators+=========================++Define sized random generators for almost any type.++```haskell+    {-# LANGUAGE DeriveDataTypeable #-}+    import Data.Data+    import Test.QuickCheck+    import Data.Random.Generics++    data Term = Lambda Int Term | App Term Term | Var Int+      deriving (Show, Data)++    instance Arbitrary Term where+      arbitrary = sized $ generatorPWith [positiveInts]++    positiveInts :: Alias Gen+    positiveInts =+      alias $ \() -> fmap getPositive arbitrary :: Gen Int++    main = sample (arbitrary :: Gen Term)+```++- Objects of the same size (number of constructors) occur with the same+  probability (see Duchon et al., references below).+- Implements rejection sampling and pointing.+- Works with QuickCheck and MonadRandom.+- Can be extended or modified with user defined generators.++References+----------++- The core theory of Boltzmann samplers is described in+  [Boltzmann Samplers for the Random Generation of Combinatorial Structures](http://algo.inria.fr/flajolet/Publications/DuFlLoSc04.pdf),+  P. Duchon, P. Flajolet, G. Louchard, G. Schaeffer.++- The numerical evaluation of recursively defined generating functions+  is taken from+  [Boltzmann Oracle for Combinatorial Systems](http://www.dmtcs.org/pdfpapers/dmAI0132.pdf),+  C. Pivoteau, B. Salvy, M. Soria.
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ bench/binaryTree.hs view
@@ -0,0 +1,78 @@+{-# LANGUAGE DeriveDataTypeable, DeriveGeneric #-}+module Main where++import Control.Applicative+import Control.Monad+import Control.Monad.Trans.Class+import Data.Bool+import Data.Data+import Data.Functor+import GHC.Generics+import Control.DeepSeq+import Criterion.Main+import Test.QuickCheck+import Test.QuickCheck.Gen+import Test.QuickCheck.Random+import Control.Exception ( evaluate )+import Data.Random.Generics+import Data.Random.Generics.Internal+import Data.Random.Generics.Internal.Types++data T = N T T | L+  deriving (Eq, Ord, Show, Data, Generic)++instance NFData T++gen1 :: Int -> Gen T+gen1 n = runRejectT (tolerance epsilon (n + 1)) gen'+  where+    gen' = incr >> lift arbitrary >>= bool (return L) (liftA2 N gen' gen')++gen2 :: Int -> Gen T+gen2 n = g+  where+    (minSize, maxSize) = tolerance epsilon (n + 1)+    g = gen' 0 (\m t -> if m < minSize then g else return t)+    gen' n k | n >= maxSize = g+    gen' n k =+      arbitrary >>= bool+        (k (n+1) L)+        (gen' (n+1) $ \m l -> gen' m $ \m r -> k m (N l r))++main = getGs >>= \gs -> defaultMain $ liftA2 (\n f -> f n gs)+  [4 ^ e | e <- [1 .. 5]]++  -- Singular rejection sampling+  [ bg "handwritten1" gen1+  , bg "handwritten2" gen2+  , bg "SR" generatorSR++  -- Sized rejection sampling+  , bg "R" generatorR'++  -- Sized rejection sampling, not memoizing oracle+  , bg' "R-recomp" generatorR'++  -- Pointed generator+  , bg "P" generatorP'++  -- Pointed generator with rejection sampling+  , bg "PR" generatorPR'++  -- Pointed generator, not memoizing oracle+  , bg' "P-recomp" generatorP'+  ]++bg, bg' :: String -> (Int -> Gen T) -> Int -> [QCGen] -> Benchmark+bg name gen n gs =+  bench (name ++ "_" ++ show n) $+    nf (fmap (\g -> unGen gg g 0)) gs+  where+    gg = gen n++bg' name gen n gs =+  bench (name ++ "_" ++ show n) $+    nf (fmap (\(n, g) -> unGen (gen n) g 0)) (fmap ((,) n) gs)++getGs :: IO [QCGen]+getGs = replicateM 100 newQCGen
+ generic-random.cabal view
@@ -0,0 +1,65 @@+name:                generic-random+version:             0.1.0.0+synopsis:            Generic random generators+description:         Please see the README below.+homepage:            http://github.com/lysxia/generic-random+license:             MIT+license-file:        LICENSE+stability:           Experimental+author:              Li-yao Xia+maintainer:          lysxia@gmail.com+category:            Generics, Testing+build-type:          Simple+extra-source-files:  README.md+cabal-version:       >=1.10++library+  hs-source-dirs:      src+  exposed-modules:+    Data.Random.Generics+    Data.Random.Generics.Internal+    Data.Random.Generics.Internal.Oracle+    Data.Random.Generics.Internal.Solver+    Data.Random.Generics.Internal.Types+  build-depends:+    base >= 4.8 && < 5,+    containers,+    hashable,+    unordered-containers,+    ieee754,+    ad,+    hmatrix,+    vector,+    mtl,+    transformers,+    MonadRandom,+    QuickCheck+  default-language:    Haskell2010+  ghc-options: -Wall -fno-warn-name-shadowing++test-suite test-tree+  type:             exitcode-stdio-1.0+  hs-source-dirs:   test+  main-is:          tree.hs+  default-language: Haskell2010+  build-depends:+    base,+    QuickCheck,+    generic-random++benchmark bench-binarytree+  type:             exitcode-stdio-1.0+  hs-source-dirs:   bench+  main-is:          binaryTree.hs+  default-language: Haskell2010+  build-depends:+    base,+    criterion,+    deepseq,+    QuickCheck,+    transformers,+    generic-random++source-repository head+  type:     git+  location: https://github.com/lyxia/generic-random
+ src/Data/Random/Generics.hs view
@@ -0,0 +1,302 @@+-- | Generic Boltzmann samplers.+--+-- Here, the words "/sampler/" and "/generator/" are used interchangeably.+--+-- Given an algebraic datatype:+--+-- > data A = A1 B C | A2 D+--+-- a Boltzmann sampler is recursively defined by choosing a constructor with+-- some fixed distribution, and /independently/ generating values for the+-- corresponding fields with the same method.+--+-- A key component is the aforementioned distribution, defined for every type+-- such that the resulting generator produces a finite value in the end. These+-- distributions are obtained from a precomputed object called /oracle/, which+-- we will not describe further here.+--+-- Oracles depend on the target size of the generated data (except for singular+-- samplers), and can be fairly expensive to compute repeatedly, hence some of+-- the functions below attempt to avoid (re)computing too many of them even+-- when the required size changes.+--+-- When these functions are specialized, oracles are memoized and will be+-- reused for different sizes.++module Data.Random.Generics (+  Size',+  -- * Main functions+  -- $sized+  generatorSR,+  generatorP,+  generatorPR,+  generatorR,+  -- ** Fixed size+  -- $fixed+  generatorP',+  generatorPR',+  generatorR',+  generator',+  -- * Generators with aliases+  -- $aliases+  generatorSRWith,+  generatorPWith,+  generatorPRWith,+  generatorRWith,+  -- ** Fixed size+  generatorPWith',+  generatorPRWith',+  generatorRWith',+  generatorWith',+  -- * Other generators+  -- $other+  Points,+  generatorM,+  generatorMR,+  generator_,+  generatorR_,+  -- * Auxiliary definitions+  -- ** Type classes+  MonadRandomLike (..),+  AMonadRandom (..),+  -- ** Alias+  alias,+  aliasR,+  coerceAlias,+  coerceAliases,+  Alias (..),+  AliasR,+  ) where++import Data.Data+import Data.Random.Generics.Internal+import Data.Random.Generics.Internal.Types++-- * Main functions++-- $sized+--+-- === Suffixes+--+-- [@S@] Singular sampler.+--+--     This works with recursive tree-like structures, as opposed to (lists of)+--     structures with bounded size. More precisely, the generating function of+--     the given type should have a finite radius of convergence, with a+--     singularity of a certain kind (see Duchon et al., reference in the+--     README), so that the oracle can be evaluated at that point.+--+--     This has the advantage of using the same oracle for all size parameters,+--     which simply specify a target size interval.+--+-- [@P@] Generator of pointed values.+--+--     It usually has a flatter distribution of sizes than a simple Boltzmann+--     sampler, making it an efficient alternative to rejection sampling.+--+--     It also works on more types, particularly lists and finite types,+--     but relies on multiple oracles.+--+-- [@R@] Rejection sampling.+--+--     These generators filter out values whose sizes are not within some+--     interval. In the first two sections, that interval is implicit:+--     @[(1-'epsilon')*size', (1+'epsilon')*size']@, for @'epsilon' = 0.1@.+--+--     The generator restarts as soon as it has produced more constructors than+--     the upper bound, this strategy is called /ceiled rejection sampling/.+--+-- = Pointing+--+-- The /pointing/ of a type @t@ is a derived type whose values are essentially+-- values of type @t@, with one of their constructors being "pointed".+-- Alternatively, we may turn every constructor into variants that indicate+-- the position of points.+--+-- @+--   -- Original type+--   data Tree = Node Tree Tree | Leaf+--   -- Pointing of Tree+--   data Tree'+--     = Tree' Tree -- Point at the root+--     | Node'0 Tree' Tree -- Point to the left+--     | Node'1 Tree Tree' -- Point to the right+-- @+--+-- Pointed values are easily mapped back to the original type by erasing the+-- point. Pointing makes larger values occur much more frequently, while+-- preserving the uniformness of the distribution conditionally to a fixed+-- size.+--++-- | @+--   'generatorSR' :: Int -> 'Gen' a+--   'asMonadRandom' . 'generatorSR' :: 'MonadRandom' m => Int -> m a+-- @+--+-- Singular ceiled rejection sampler.+generatorSR :: (Data a, MonadRandomLike m) => Size' -> m a+generatorSR = generatorSRWith []++-- | @+--   'generatorP' :: Int -> 'Gen' a+--   'asMonadRandom' . 'generatorP' :: 'MonadRandom' m => Int -> m a+-- @+--+-- Generator of pointed values.++generatorP :: (Data a, MonadRandomLike m) => Size' -> m a+generatorP = generatorPWith []++-- | Pointed generator with rejection.+generatorPR :: (Data a, MonadRandomLike m) => Size' -> m a+generatorPR = generatorPRWith []++-- | Generator with rejection and dynamic average size.+generatorR :: (Data a, MonadRandomLike m) => Size' -> m a+generatorR = generatorRWith []++-- ** Fixed size++-- $fixed+-- The @'@ suffix indicates functions which do not do any+-- precomputation before passing the size parameter.+--+-- This means that oracles are computed from scratch for every size value,+-- which may incur a significant overhead.++-- | Pointed generator.+generatorP' :: (Data a, MonadRandomLike m) => Size' -> m a+generatorP' = generatorPWith' []++-- | Pointed generator with rejection.+generatorPR' :: (Data a, MonadRandomLike m) => Size' -> m a+generatorPR' = generatorPRWith' []++-- | Ceiled rejection sampler with given average size.+generatorR' :: (Data a, MonadRandomLike m) => Size' -> m a+generatorR' = generatorRWith' []++-- | Basic boltzmann sampler with no optimization.+generator' :: (Data a, MonadRandomLike m) => Size' -> m a+generator' = generatorWith' []++-- * Generators with aliases++-- $aliases+-- Boltzmann samplers can normally be defined only for types @a@ such that:+--+-- - they are instances of 'Data';+-- - the set of types of subterms of values of type @a@ is finite;+-- - and all of these types have at least one finite value (i.e., values with+--   finitely many constructors).+--+-- Examples of misbehaving types are:+--+-- - @a -> b -- Not Data@+-- - @data E a = L a | R (E [a]) -- Contains a, [a], [[a]], [[[a]]], etc.@+-- - @data I = C I -- No finite value@+--+-- = Alias+--+-- The 'Alias' type works around these limitations ('AliasR' for rejection+-- samplers).+-- This existential wrapper around a user-defined function @f :: a -> m b@+-- makes @generic-random@ view occurences of the type @b@ as @a@ when+-- processing a recursive system of types, possibly stopping some infinite+-- unrolling of type definitions. When a value of type @b@ needs to be+-- generated, it generates an @a@ which is passed to @f@.+--+-- @+--   let+--     as = ['aliasR' $ \\() -> return (L []) :: 'Gen' (E [[Int]])]+--   in+--     'generatorSRWith' as 'asGen' :: 'Size' -> 'Gen' (E Int)+-- @+--+-- Another use case is to plug in user-defined generators where the default is+-- not satisfactory, for example, to get positive @Int@s:+--+-- @+--   let+--     as = ['alias' $ \\() -> 'choose' (0, 100) :: 'Gen' Int)]+--   in+--     'generatorPWith' as 'asGen' :: 'Size' -> 'Gen' [Int]+-- @++generatorSRWith+  :: (Data a, MonadRandomLike m) => [AliasR m] -> Size' -> m a+generatorSRWith aliases =+  generatorR_ aliases 0 Nothing . tolerance epsilon++generatorPRWith+  :: (Data a, MonadRandomLike m) => [AliasR m] -> Size' -> m a+generatorPRWith aliases size' =+  generatorMR aliases 1 size' (tolerance epsilon size')++generatorPWith+  :: (Data a, MonadRandomLike m) => [Alias m] -> Size' -> m a+generatorPWith aliases = generatorM aliases 1++generatorRWith+  :: (Data a, MonadRandomLike m) => [AliasR m] -> Size' -> m a+generatorRWith aliases size' =+  generatorMR aliases 0 size' (tolerance epsilon size')++-- ** Fixed size++generatorPWith'+  :: (Data a, MonadRandomLike m) => [Alias m] -> Size' -> m a+generatorPWith' aliases = generator_ aliases 1 . Just++generatorPRWith'+  :: (Data a, MonadRandomLike m) => [AliasR m] -> Size' -> m a+generatorPRWith' aliases size' =+  generatorR_ aliases 1 (Just size') (tolerance epsilon size')++generatorRWith'+  :: (Data a, MonadRandomLike m) => [AliasR m] -> Size' -> m a+generatorRWith' aliases size' =+  generatorR_ aliases 0 (Just size') (tolerance epsilon size')++generatorWith'+  :: (Data a, MonadRandomLike m) => [Alias m] -> Size' -> m a+generatorWith' aliases = generator_ aliases 0 . Just++-- * Other generators++-- $other Used in the implementation of the generators above.+-- These also allow to apply pointing more than once.+--+-- === Suffixes+--+-- [@M@] Sized generators are memoized for some sparsely chosen values of+-- sizes. Subsequently supplied sizes are approximated by the closest larger+-- value. This strategy avoids recomputing too many oracles. Aside from+-- singular samplers, all other generators above not marked by @'@ use this.+--+-- [@_@] If the size parameter is @Nothing@, produces the singular generator+-- (associated with the suffix @S@); otherwise the generator produces values+-- with average size equal to the given value.++generatorM+  :: (Data a, MonadRandomLike m)+  => [Alias m] -> Points -> Size' -> m a+generatorM = memo make apply++generatorMR+  :: (Data a, MonadRandomLike m)+  => [AliasR m] -> Points -> Size' -> (Size', Size') -> m a+generatorMR = memo makeR applyR++-- | Boltzmann sampler without rejection.+generator_+  :: (Data a, MonadRandomLike m)+  => [Alias m] -> Points -> Maybe Size' -> m a+generator_ aliases = apply (make aliases [])++-- | Boltzmann sampler with rejection.+generatorR_+  :: (Data a, MonadRandomLike m)+  => [AliasR m] -> Points -> Maybe Size' -> (Size', Size') -> m a+generatorR_ aliases = applyR (makeR aliases [])
+ src/Data/Random/Generics/Internal.hs view
@@ -0,0 +1,146 @@+{-# LANGUAGE RecordWildCards, DeriveFunctor #-}+module Data.Random.Generics.Internal where++import Control.Arrow ( (&&&) )+import Control.Applicative+import Data.Data+import Data.Foldable+import Data.Maybe+import qualified Data.HashMap.Lazy as HashMap+import Data.Random.Generics.Internal.Oracle+import Data.Random.Generics.Internal.Types++-- | Sized generator.+data SG r = SG+  { minSize :: Size+  , maxSizeM :: Maybe Size+  , runSG :: Points -> Maybe Double -> r+  , runSmallG :: Points -> r+  } deriving Functor++-- | Number of pointing iterations.+type Points = Int++rangeSG :: SG r -> (Size, Maybe Size)+rangeSG = minSize &&& maxSizeM++-- | For documentation.+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++-- * Helper functions++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)++-- | The size of a value is its number of constructors.+--+-- Here, however, the 'Size'' type is interpreted differently to make better+-- use of QuickCheck's size parameter provided by the 'Test.QuickCheck.sized'+-- combinator, so that we generate non-trivial data even at very small size+-- values.+--+-- For infinite types, with objects of unbounded sizes @> minSize@, given a+-- parameter @delta :: 'Size''@, the produced values have an average size close+-- to @minSize + delta@.+--+-- For example, values of type @Either () [Bool]@ have at least two constructors,+-- so+--+-- @+--   'generator' delta :: 'Gen' (Either () [Bool])+-- @+--+-- will target sizes close to @2 + delta@;+-- the offset becomes less noticeable as @delta@ grows to infinity.+--+-- For finite types with sizes in @[minSize, maxSize]@, the target expected+-- size is obtained by clamping a 'Size'' to @[0, 99]@ and applying an affine+-- mapping.+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' = 0.1+--+-- Default approximation ratio.+epsilon :: Double+epsilon = 0.1++-- | > (size * (1 - epsilon), size * (1 + epsilon))+tolerance :: Double -> Int -> (Int, Int)+tolerance epsilon size = (size - delta, size + delta)+  where+    delta = ceiling (fromIntegral size * epsilon)++-- * Auxiliary definitions++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)++-- Oracles are computed only for sizes that are a power of two away from+-- the minimum size of the datatype @minSize + 2 ^ e@.+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)
+ src/Data/Random/Generics/Internal/Oracle.hs view
@@ -0,0 +1,541 @@+{-# LANGUAGE FlexibleContexts, GADTs, RankNTypes, ScopedTypeVariables #-}+{-# LANGUAGE DeriveGeneric, ImplicitParams #-}+{-# LANGUAGE RecordWildCards, DeriveDataTypeable #-}+module Data.Random.Generics.Internal.Oracle where++import Control.Applicative+import Control.Monad+import Control.Monad.Fix+import Control.Monad.Reader+import Control.Monad.State+import Data.Bifunctor+import Data.Data+import Data.Hashable ( Hashable )+import Data.HashMap.Lazy ( HashMap )+import qualified Data.HashMap.Lazy as HashMap+import Data.Maybe ( fromJust, isJust )+import Data.Monoid+import qualified Data.Vector as V+import qualified Data.Vector.Storable as S+import GHC.Generics ( Generic )+import GHC.Stack ( CallStack, showCallStack )+import Numeric.AD+import Data.Random.Generics.Internal.Types+import Data.Random.Generics.Internal.Solver++-- | We build a dictionary which reifies type information in order to+-- create a Boltzmann generator.+--+-- We denote by @n@ (or 'count') the number of types in the dictionary.+--+-- Every type has an index @0 <= i < n@; the variable @X i@ represents its+-- generating function @C_i(x)@, and @X (i + k*n)@ the GF of its @k@-th+-- "pointing" @C_i[k](x)@; we have+--+-- @+--   C_i[0](x) = C_i(x)+--   C_i[k+1](x) = x * C_i[k]'(x)+-- @+--+-- where @C_i[k]'@ is the derivative of @C_i[k]@. See also 'point'.+--+-- The /order/ (or /valuation/) of a power series is the index of the first+-- non-zero coefficient, called the /leading coefficient/.++data DataDef m = DataDef+  { count :: Int -- ^ Number of registered types+  , points :: Int -- ^ Number of iterations of the pointing operator+  , index :: HashMap TypeRep (Either Aliased Ix) -- ^ Map from types to indices+  , xedni :: HashMap Ix SomeData' -- ^ Inverse map from indices to types+  , xedni' :: HashMap Aliased (Ix, Alias m) -- ^ Inverse map to aliases+  , types :: HashMap C [(Integer, Constr, [C'])]+  -- ^ Structure of types and their pointings (up to 'points', initially 0)+  --+  -- Primitive types and empty types are mapped to an empty constructor list, and+  -- can be distinguished using 'Data.Data.dataTypeRep' on the 'SomeData'+  -- associated to it by 'xedni'.+  --+  -- The integer is a multiplicity which can be > 1 for pointings.+  , lTerm :: HashMap Ix (Nat, Integer)+  -- ^ Leading term @a * x ^ u@ of the generating functions @C_i[k](x)@ in the+  -- form (u, a).+  --+  -- [Order @u@] Smallest size of objects of a given type.+  -- [Leading coefficient @a@] number of objects of smallest size.+  , degree :: HashMap Ix Int+  -- ^ Degrees of the generating functions, when applicable: greatest size of+  -- objects of a given type.+  } deriving Show++-- | A pair @C i k@ represents the @k@-th "pointing" of the type at index @i@,+-- with generating function @C_i[k](x)@.+data C = C Ix Int+  deriving (Eq, Ord, Show, Generic)++instance Hashable C++data AC = AC Aliased Int+  deriving (Eq, Ord, Show, Generic)++instance Hashable AC++type C' = (Maybe Aliased, C)++newtype Aliased = Aliased Int+  deriving (Eq, Ord, Show, Generic)++instance Hashable Aliased++type Ix = Int++data Nat = Zero | Succ Nat+  deriving (Eq, Ord, Show)++instance Monoid Nat where+  mempty = Zero+  mappend (Succ n) = Succ . mappend n+  mappend Zero = id++natToInt :: Nat -> Int+natToInt Zero = 0+natToInt (Succ n) = 1 + natToInt n++infinity :: Nat+infinity = Succ infinity++dataDef :: [Alias m] -> DataDef m+dataDef as = DataDef+  { count = 0+  , points = 0+  , index = index+  , xedni = HashMap.empty+  , xedni' = xedni'+  , types = HashMap.empty+  , lTerm = HashMap.empty+  , degree = HashMap.empty+  } where+    xedni' = HashMap.fromList (fmap (\(i, a) -> (i, (-1, a))) as')+    index = HashMap.fromList (fmap (\(i, a) -> (ofType a, Left i)) as')+    as' = zip (fmap Aliased [0 ..]) as+    ofType (Alias f) = typeRep (f undefined)++-- | Find all types that may be types of subterms of a value of type @a@.+--+-- This will loop if there are infinitely many such types.+collectTypes :: Data a => [Alias m] -> proxy a -> DataDef m+collectTypes as a = collectTypesM a `execState` dataDef as++-- | Primitive datatypes have @C(x) = x@: they are considered as+-- having a single object (@lCoef@) of size 1 (@order@)).+primOrder :: Int+primOrder = 1++primOrder' :: Nat+primOrder' = Succ Zero++primlCoef :: Integer+primlCoef = 1++-- | The type of the first argument of 'Data.Data.gunfold'.+type GUnfold m = forall b r. Data b => m (b -> r) -> m r++-- | Type of 'xedni''.+type AMap m = HashMap Aliased (Ix, Alias m)++collectTypesM :: Data a => proxy a+  -> State (DataDef m) (Either Aliased Ix, ((Nat, Integer), Maybe Int))+collectTypesM a = chaseType a (const id)++chaseType :: Data a => proxy a+  -> ((Maybe (Alias m), Ix) -> AMap m -> AMap m)+  -> State (DataDef m) (Either Aliased Ix, ((Nat, Integer), Maybe Int))+chaseType a k = do+  let t = typeRep a+  dd@DataDef{..} <- get+  let+    lookup i r =+      let+        lTerm_i = lTerm #! i+        degree_i = HashMap.lookup i degree+      in return (r, (lTerm_i, degree_i))+  case HashMap.lookup t index of+    Nothing -> do+      let i = count+      put dd+        { count = i + 1+        , index = HashMap.insert t (Right i) index+        , xedni = HashMap.insert i (someData' a) xedni+        , xedni' = k (Nothing, i) xedni'+        }+      traverseType a i -- Updates lTerm and degree+    Just (Right i) -> do+      put dd { xedni' = k (Nothing, i) xedni' }+      lookup i (Right i)+    Just (Left j) ->+      case xedni' #! j of+        (-1, Alias f) -> do+          (_, ld) <- chaseType (ofType f) $ \(alias, i) ->+            let+              alias' = case alias of+                Nothing -> Alias f+                Just (Alias g) -> Alias (composeCastM f g)+            in+            k (Just alias', i) . HashMap.insert j (i, alias')+          return (Left j, ld)+        (i, _) -> lookup i (Left j)+  where+    ofType :: (m a -> m b) -> m a+    ofType _ = undefined++-- | Traversal of the definition of a datatype.+traverseType+  :: Data a => proxy a -> Ix+  -> State (DataDef m) (Either Aliased Ix, ((Nat, Integer), Maybe Int))+traverseType a i = do+  let d = withProxy dataTypeOf a+  mfix $ \ ~(_, (lTerm_i0, _)) -> do+    modify $ \dd@DataDef{..} -> dd+      { lTerm = HashMap.insert i lTerm_i0 lTerm+      }+    (types_i, ld@(_, degree_i)) <- traverseType' a d+    modify $ \dd@DataDef{..} -> dd+      { types = HashMap.insert (C i 0) types_i types+      , degree = maybe id (HashMap.insert i) degree_i degree+      }+    return (Right i, ld)++traverseType'+  :: Data a => proxy a -> DataType+  -> State (DataDef m)+      ([(Integer, Constr, [(Maybe Aliased, C)])], ((Nat, Integer), Maybe Int))+traverseType' a d | isAlgType d = do+  let+    constrs = dataTypeConstrs d+    collect+      :: GUnfold (StateT+        ([Either Aliased Ix], (Nat, Integer), Maybe Int)+        (State (DataDef m)))+    collect mkCon = do+      f <- mkCon+      let ofType :: (b -> a) -> Proxy b+          ofType _ = Proxy+          b = ofType f+      (j, (lTerm_, degree_)) <- lift (collectTypesM b)+      modify $ \(js, lTerm', degree') ->+        (j : js, lMul lTerm_ lTerm', liftA2 (+) degree_ degree')+      return (withProxy f b)+  tlds <- forM constrs $ \constr -> do+    (js, lTerm', degree') <-+      gunfold collect return constr `proxyType` a+        `execStateT` ([], (Zero, 1), Just 1)+    dd <- get+    let+      c (Left j) = (Just j, C (fst (xedni' dd #! j)) 0)+      c (Right i) = (Nothing, C i 0)+    return ((1, constr, [ c j | j <- js]), lTerm', degree')+  let+    (types_i, ls, ds) = unzip3 tlds+    lTerm_i = first Succ (lSum ls)+    degree_i = maxDegree ds+  return (types_i, (lTerm_i, degree_i))+traverseType' _ _ =+  return ([], ((primOrder', primlCoef), Just primOrder))++-- | If @(u, a)@ represents a power series of leading term @a * x ^ u@, and+-- similarly for @(u', a')@, this finds the leading term of their sum.+--+-- The comparison of 'Nat' is unrolled here for maximum laziness.+lPlus :: (Nat, Integer) -> (Nat, Integer) -> (Nat, Integer)+lPlus (Zero, lCoef) (Zero, lCoef') = (Zero, lCoef + lCoef')+lPlus (Zero, lCoef) _ = (Zero, lCoef)+lPlus _ (Zero, lCoef') = (Zero, lCoef')+lPlus (Succ order, lCoef) (Succ order', lCoef') =+  first Succ $ lPlus (order, lCoef) (order', lCoef')++-- | Sum of a list of series.+lSum :: [(Nat, Integer)] -> (Nat, Integer)+lSum [] = (infinity, 0)+lSum ls = foldl1 lPlus ls++-- | Leading term of a product of series.+lMul :: (Nat, Integer) -> (Nat, Integer) -> (Nat, Integer)+lMul (order, lCoef) (order', lCoef') = (order <> order', lCoef * lCoef')++lProd :: [(Nat, Integer)] -> (Nat, Integer)+lProd = foldl lMul (Zero, 1)++maxDegree :: [Maybe Int] -> Maybe Int+maxDegree = foldl (liftA2 max) (Just minBound)++-- | Pointing operator.+--+-- Populates a 'DataDef' with one more level of pointings.+-- ('collectTypes' produces a dictionary at level 0.)+--+-- The "pointing" of a type @t@ is a derived type whose values are essentially+-- values of type @t@, with one of their constructors being "pointed".+-- Alternatively, we may turn every constructor into variants that indicate+-- the position of points.+--+-- @+--   -- Original type+--   data Tree = Node Tree Tree | Leaf+--   -- Pointing of Tree+--   data Tree'+--     = Tree' Tree -- Point at the root+--     | Node'0 Tree' Tree -- Point to the left+--     | Node'1 Tree Tree' -- Point to the right+--   -- Pointing of the pointing+--   -- Notice that the "points" introduced by both applications of pointing+--   -- are considered different: exchanging their positions (when different)+--   -- produces a different tree.+--   data Tree''+--     = Tree'' Tree' -- Point 2 at the root, the inner Tree' places point 1+--     | Node'0' Tree' Tree -- Point 1 at the root, point 2 to the left+--     | Node'1' Tree Tree' -- Point 1 at the root, point 2 to the right+--     | Node'0'0 Tree'' Tree -- Points 1 and 2 to the left+--     | Node'0'1 Tree' Tree' -- Point 1 to the left, point 2 to the right+--     | Node'1'0 Tree' Tree' -- Point 1 to the right, point 2 to the left+--     | Node'0'1 Tree Tree'' -- Points 1 and 2 to the right+-- @+--+-- If we ignore points, some constructors are equivalent. Thus we may simply+-- calculate their multiplicity instead of duplicating them.+--+-- Given a constructor with @c@ arguments @C x_1 ... x_c@, and a sequence+-- @p_0 + p_1 + ... + p_c = k@ corresponding to a distribution of @k@ points+-- (@p_0@ are assigned to the constructor @C@ itself, and for @i > 0@, @p_i@+-- points are assigned within the @i@-th subterm), the multiplicity of the+-- constructor paired with that distribution is the multinomial coefficient+-- @multinomial k [p_1, ..., p_c]@.++point :: DataDef m -> DataDef m+point dd@DataDef{..} = dd+  { points = points'+  , types = foldl g types [0 .. count-1]+  } where+    points' = points + 1+    g types i = HashMap.insert (C i points') (types' i) types+    types' i = types #! C i 0 >>= h+    h (_, constr, js) = do+      ps <- partitions points' (length js)+      let+        mult = multinomial points' ps+        js' = zipWith (\(j', C i _) p -> (j', C i p)) js ps+      return (mult, constr, js')++-- | An oracle gives the values of the generating functions at some @x@.+type Oracle = HashMap C Double++-- | Find the value of @x@ such that the average size of the generator+-- for the @k-1@-th pointing is equal to @size@, and produce the associated+-- oracle. If the size is @Nothing@, find the radius of convergence.+--+-- The search evaluates the generating functions for some values of @x@ in+-- order to run a binary search. The evaluator is implemented using Newton's+-- method, the convergence of which has been shown for relevant systems in+-- /Boltzmann Oracle for Combinatorial Systems/,+-- C. Pivoteau, B. Salvy, M. Soria.+makeOracle :: DataDef m -> TypeRep -> Maybe Double -> Oracle+makeOracle dd0 t size' =+  seq v+  HashMap.fromList (zip cs (S.toList v))+  where+    -- We need the next pointing to capture the average size in an equation.+    dd@DataDef{..} = if isJust size' then point dd0 else dd0+    cs = flip C <$> [0 .. points] <*> [0 .. count - 1]+    m = count * (points + 1)+    k = points - 1+    i = case index #! t of+      Left j -> fst (xedni' #! j)+      Right i -> i+    checkSize _ (Just ys) | S.any (< 0) ys = False+    -- There may be solutions outside of the radius+    -- of convergence, but with negative components.+    checkSize (Just size) (Just ys) =+      size >= size_+      where+        size_ = ys S.! j' / ys S.! j+        j = dd ? C i k+        j' = dd ? C i (k + 1)+    checkSize Nothing (Just _) = True+    checkSize _ Nothing = False+    -- Equations defining C_i(x) for all types with indices i+    phis :: Num a => V.Vector (a -> V.Vector a -> a)+    phis = V.fromList [ phi dd c (types #! c) | c <- listCs dd ]+    eval' x = fixedPoint defSolveArgs phi' (S.replicate m 0)+      where+        phi' :: (Mode a, Scalar a ~ Double) => V.Vector a -> V.Vector a+        phi' y = fmap (\f -> f (auto x) y) phis+    v = fromJust (search eval' (checkSize size'))++-- | Generating function definition. This defines a @Phi_i[k]@ function+-- associated with the @k@-th pointing of the type at index @i@, such that:+--+-- > C_i[k](x)+-- >   = Phi_i[k](x, C_0[0](x), ..., C_(n-1)[0](x),+-- >              ..., C_0[k](x), ..., C_(n-1)[k](x))+--+-- Primitive datatypes have @C(x) = x@: they are considered as+-- having a single object ('lCoef') of size 1 ('order')).+phi :: Num a => DataDef m -> C -> [(Integer, constr, [C'])]+  -> a -> V.Vector a -> a+phi DataDef{..} (C i _) [] =+  case xedni #! i of+    SomeData a ->+      case (dataTypeRep . withProxy dataTypeOf) a of+        AlgRep _ -> \_ _ -> 0+        _ -> \x _ -> fromInteger primlCoef * x ^ primOrder+phi dd@DataDef{..} _ tyInfo = f+  where+    f x y = x * (sum . fmap (toProd y)) tyInfo+    toProd y (w, _, js) =+      fromInteger w * product [ y V.! (dd ? j) | (_, j) <- js ]++-- | Maps a key representing a type @a@ (or one of its pointings) to a+-- generator @m a@.+type Generators m = (HashMap AC (SomeData m), HashMap C (SomeData m))++-- | Build all involved generators at once.+makeGenerators+  :: forall m. MonadRandomLike m+  => DataDef m -> Oracle -> Generators m+makeGenerators DataDef{..} oracle =+  seq oracle+  (generatorsL, generatorsR)+  where+    f (C i _) tyInfo = case xedni #! i of+      SomeData a -> SomeData $ incr >>+        case tyInfo of+          [] -> defGen+          _ -> frequencyWith doubleR (fmap g tyInfo) `proxyType` a+    g :: Data a => (Integer, Constr, [C']) -> (Double, m a)+    g (v, constr, js) =+      ( fromInteger v * w+      , gunfold generate return constr `runReaderT` gs)+      where+        gs = fmap (\(j', i) -> m j' i) js+        m = maybe (generatorsR #!) m'+        m' j (C _ k) = (generatorsL #! AC j k)+        w = product $ fmap ((oracle #!) . snd) js+    h (j, (i, Alias f)) k =+      (AC j k, applyCast f (generatorsR #! C i k))+    generatorsL = HashMap.fromList (liftA2 h (HashMap.toList xedni') [0 .. points])+    generatorsR = HashMap.mapWithKey f types++type SmallGenerators m =+  (HashMap Aliased (SomeData m), HashMap Ix (SomeData m))++-- | Generators of values of minimal sizes.+smallGenerators+  :: forall m. MonadRandomLike m => DataDef m -> SmallGenerators m+smallGenerators DataDef{..} = (generatorsL, generatorsR)+  where+    f i (SomeData a) = SomeData $ incr >>+      case types #! C i 0 of+        [] -> defGen+        tyInfo ->+          let gs = (tyInfo >>= g (fst (lTerm #! i))) in+          frequencyWith integerR gs `proxyType` a+    g :: Data a => Nat -> (Integer, Constr, [C']) -> [(Integer, m a)]+    g minSize (_, constr, js) =+      guard (minSize == Succ size) *>+      [(weight, gunfold generate return constr `runReaderT` gs)]+      where+        (size, weight) = lProd [ lTerm #! i | (_, C i _) <- js ]+        gs = fmap lookup js+        lookup (j', C i _) = maybe (generatorsR #! i) (generatorsL #!) j'+    h (j, (i, Alias f)) = (j, applyCast f (generatorsR #! i))+    generatorsL = (HashMap.fromList . fmap h . HashMap.toList) xedni'+    generatorsR = HashMap.mapWithKey f xedni++generate :: Applicative m => GUnfold (ReaderT [SomeData m] m)+generate rest = ReaderT $ \(g : gs) ->+  rest `runReaderT` gs <*> unSomeData g++defGen :: (Data a, MonadRandomLike m) => m a+defGen = gen+  where+    gen =+      let dt = withProxy dataTypeOf gen in+      case dataTypeRep dt of+        IntRep -> fromConstr . mkIntegralConstr dt <$> int+        FloatRep -> fromConstr . mkRealConstr dt <$> double+        CharRep -> fromConstr . mkCharConstr dt <$> char+        AlgRep _ -> error "Cannot generate for empty type."+        NoRep -> error "No representation."++-- * Short operators++(?) :: DataDef m -> C -> Int+dd ? C i k = i + k * count dd++-- | > dd ? (listCs dd !! i) = i+listCs :: DataDef m -> [C]+listCs dd = liftA2 (flip C) [0 .. points dd] [0 .. count dd - 1]++ix :: C -> Int+ix (C i _) = i++-- | > dd ? (dd ?! i) = i+(?!) :: DataDef m -> Int -> C+dd ?! j = C i k+  where (k, i) = j `divMod` count dd++getGenerator :: (Functor m, Data a)+  => DataDef m -> Generators m -> proxy a -> Int -> m a+getGenerator dd (l, r) a k = unSomeData $+  case index dd #! typeRep a of+    Right i -> (r #! C i k)+    Left j -> (l #! AC j k)++getSmallGenerator :: (Functor m, Data a)+  => DataDef m -> SmallGenerators m -> proxy a -> m a+getSmallGenerator dd (l, r) a = unSomeData $+  case index dd #! typeRep a of+    Right i -> (r #! i)+    Left j -> (l #! j)++-- * General helper functions++frequencyWith+  :: (Show r, Ord r, Num r, Monad m) => (r -> m r) -> [(r, m a)] -> m a+frequencyWith _ [(_, a)] = a+frequencyWith randomR as = randomR total >>= select as+  where+    total = (sum . fmap fst) as+    select ((w, a) : as) x+      | x < w = a+      | otherwise = select as (x - w)+    select _ _ = (snd . head) as+    -- That should not happen in theory, but floating point might be funny.++(#!) :: (?loc :: CallStack, Eq k, Hashable k)+  => HashMap k v -> k -> v+h #! k = HashMap.lookupDefault (e ?loc) k h+  where+    e loc = error ("HashMap.(!): key not found\n" ++ showCallStack loc)++-- | @partitions k n@: lists of non-negative integers of length @n@ with sum+-- less than or equal to @k@.+partitions :: Int -> Int -> [[Int]]+partitions _ 0 = [[]]+partitions k n = do+  p <- [0 .. k]+  (p :) <$> partitions (k - p) (n - 1)++-- | Multinomial coefficient.+--+-- > multinomial n ps == factorial n `div` product [factorial p | p <- ps]+multinomial :: Int -> [Int] -> Integer+multinomial _ [] = 1+multinomial n (p : ps) = binomial n p * multinomial (n - p) ps++-- | Binomial coefficient.+--+-- > binomial n k == factorial n `div` (factorial k * factorial (n-k))+binomial :: Int -> Int -> Integer+binomial = \n k -> pascal !! n !! k+  where+    pascal = [1] : fmap nextRow pascal+    nextRow r = zipWith (+) (0 : r) (r ++ [0])
+ src/Data/Random/Generics/Internal/Solver.hs view
@@ -0,0 +1,65 @@+-- | Solve systems of equations++{-# LANGUAGE RecordWildCards #-}+{-# LANGUAGE RankNTypes, FlexibleContexts, TypeFamilies #-}+module Data.Random.Generics.Internal.Solver where++import Control.Applicative+import Data.AEq ( (~==) )+import Numeric.AD.Mode+import Numeric.AD.Mode.Forward+import Numeric.LinearAlgebra+import qualified Data.Vector as V+import qualified Data.Vector.Storable as S++data SolveArgs = SolveArgs+  { accuracy :: Double+  , numIterations :: Int+  } deriving (Eq, Ord, Show)++defSolveArgs :: SolveArgs+defSolveArgs = SolveArgs 1e-8 20++findZero+  :: SolveArgs+  -> (forall s. V.Vector (AD s (Forward R)) -> V.Vector (AD s (Forward R)))+  -> Vector R+  -> Maybe (Vector R)+findZero SolveArgs{..} f = newton numIterations+  where+    newton 0 _ = Nothing+    newton n x+      | norm_y == 1/0 = Nothing+      | norm_y > accuracy = newton (n - 1) (x - jacobian <\> y)+      | otherwise = Just x+      where+        norm_y = norm_Inf y+        jacobian = (fromRows . V.toList . fmap (V.convert . snd)) yj+        y = (V.convert . fmap fst) yj+        yj = jacobian' f (S.convert x)++fixedPoint+  :: SolveArgs+  -> (forall a. (Mode a, Scalar a ~ R) => V.Vector a -> V.Vector a)+  -> Vector R+  -> Maybe (Vector R)+fixedPoint args f = findZero args (liftA2 (V.zipWith (-)) f id)++-- | Assuming @p . f@ is satisfied only for positive values in some interval+-- @(0, r]@, find @f r@.+search :: (Double -> a) -> (a -> Bool) -> a+search f p = search' e0 (0 : [2 ^ n | n <- [0 .. 100 :: Int]])+  where+    search' y (x : xs@(x' : _))+      | p y' = search' y' xs+      | otherwise = search'' y x x'+      where y' = f x'+    search' _ _ = error "Solution not found. Uncontradictable predicate?"+    search'' y x x'+      | x ~== x' = y+      | p y_ = search'' y_ x_ x'+      | otherwise = search'' y x x_+      where+        x_ = (x + x') / 2+        y_ = f x_+    e0 = error "Solution not found. Unsatisfiable predicate?"
+ src/Data/Random/Generics/Internal/Types.hs view
@@ -0,0 +1,193 @@+{-# LANGUAGE RankNTypes, GADTs, ScopedTypeVariables, ImplicitParams #-}+{-# LANGUAGE TypeOperators, GeneralizedNewtypeDeriving #-}+module Data.Random.Generics.Internal.Types where++import Control.Monad.Random+import Control.Monad.Trans+import Data.Coerce+import Data.Data+import Data.Function+import GHC.Stack ( CallStack, showCallStack )+import Test.QuickCheck++data SomeData m where+  SomeData :: Data a => m a -> SomeData m++type SomeData' = SomeData Proxy++-- | Dummy instance for debugging.+instance Show (SomeData m) where+  show _ = "SomeData"++data Alias m where+  Alias :: (Data a, Data b) => !(m a -> m b) -> Alias m++type AliasR m = Alias (RejectT m)++-- | Dummy instance for debugging.+instance Show (Alias m) where+  show _ = "Alias"++-- | Main constructor for 'Alias'.+alias :: (Monad m, Data a, Data b) => (a -> m b) -> Alias m+alias = Alias . (=<<)++-- | Main constructor for 'AliasR'.+aliasR :: (Monad m, Data a, Data b) => (a -> m b) -> AliasR m+aliasR = Alias . (=<<) . fmap lift++-- | > coerceAlias :: Alias m -> Alias (AMonadRandom m)+coerceAlias :: Coercible m n => Alias m -> Alias n+coerceAlias = coerce++-- | > coerceAliases :: [Alias m] -> [Alias (AMonadRandom m)]+coerceAliases :: Coercible m n => [Alias m] -> [Alias n]+coerceAliases = coerce++-- | > composeCast f g = f . g+composeCastM :: forall a b c d m+  . (?loc :: CallStack, Typeable b, Typeable c)+  => (m c -> d) -> (a -> m b) -> (a -> d)+composeCastM f g | Just Refl <- eqT :: Maybe (b :~: c) = f . g+composeCastM _ _ = castError ([] :: [b]) ([] :: [c])++castM :: forall a b m+  . (?loc :: CallStack, Typeable a, Typeable b)+  => m a -> m b+castM a | Just Refl <- eqT :: Maybe (a :~: b) = a+castM a = let x = castError a x in x++unSomeData :: (?loc :: CallStack, Typeable a) => SomeData m -> m a+unSomeData (SomeData a) = castM a++applyCast :: (Typeable a, Data b) => (m a -> m b) -> SomeData m -> SomeData m+applyCast f = SomeData . f . unSomeData++castError :: (?loc :: CallStack, Typeable a, Typeable b)+  => proxy a -> proxy' b -> c+castError a b = error $ unlines+  [ "Error trying to cast"+  , "  " ++ show (typeRep a)+  , "to"+  , "  " ++ show (typeRep b)+  , showCallStack ?loc+  ]++withProxy :: (?loc :: CallStack) => (a -> b) -> proxy a -> b+withProxy f _ =+  f (error $ "This should not be evaluated\n" ++ showCallStack ?loc)++reproxy :: proxy a -> Proxy a+reproxy _ = Proxy++proxyType :: m a -> proxy a -> m a+proxyType = const++someData' :: Data a => proxy a -> SomeData'+someData' = SomeData . reproxy++-- | Size as the number of constructors.+type Size = Int++-- | Internal transformer for rejection sampling.+--+-- > ReaderT Size (StateT Size (MaybeT m)) a+newtype RejectT m a = RejectT+  { unRejectT :: forall r. Size -> Size -> m r -> (Size -> a -> m r) -> m r+  }++instance Functor (RejectT m) where+  fmap f (RejectT go) = RejectT $ \maxSize size retry cont ->+    go maxSize size retry $ \size a -> cont size (f a)++instance Applicative (RejectT m) where+  pure a = RejectT $ \_maxSize size _retry cont ->+    cont size a+  RejectT f <*> RejectT x = RejectT $ \maxSize size retry cont ->+    f maxSize size retry $ \size f_ ->+      x maxSize size retry $ \size x_ ->+        cont size (f_ x_)++instance Monad (RejectT m) where+  RejectT x >>= f = RejectT $ \maxSize size retry cont ->+    x maxSize size retry $ \size x_ ->+      unRejectT (f x_) maxSize size retry cont++instance MonadTrans RejectT where+  lift m = RejectT $ \_maxSize size _retry cont ->+    m >>= cont size++-- | Set lower bound+runRejectT :: Monad m => (Size, Size) -> RejectT m a -> m a+runRejectT (minSize, maxSize) (RejectT m) = fix $ \go ->+  m maxSize 0 go $ \size a ->+    if size < minSize then+      go+    else+      return a+--runRejectT (minSize, maxSize) (RejectT m) = fix $ \go -> do+--  x' <- runMaybeT (m `runReaderT` maxSize `runStateT` 0)+--  case x' of+--    Just (x, size) | size >= minSize -> return x+--    _ -> go++newtype AMonadRandom m a = AMonadRandom+  { asMonadRandom :: m a+  } deriving (Functor, Applicative, Monad)++instance MonadTrans AMonadRandom where+  lift = AMonadRandom++-- ** Dictionaries++-- | @'MonadRandomLike' m@ defines basic components to build generators,+-- allowing the implementation to remain abstract over both the+-- 'Test.QuickCheck.Gen' type and 'MonadRandom' instances.+--+-- For the latter, the wrapper 'AMonadRandom' is provided to avoid+-- overlapping instances.+class Monad m => MonadRandomLike m where+  -- | Called for every constructor. Counter for ceiled rejection sampling.+  incr :: m ()+  incr = return ()++  -- | @doubleR upperBound@: generates values in @[0, upperBound]@.+  doubleR :: Double -> m Double++  -- | @integerR upperBound@: generates values in @[0, upperBound-1]@.+  integerR :: Integer -> m Integer++  -- | Default @Int@ generator.+  int :: m Int++  -- | Default @Double@ generator.+  double :: m Double++  -- | Default @Char@ generator.+  char :: m Char++instance MonadRandomLike Gen where+  doubleR x = choose (0, x)+  integerR x = choose (0, x-1)+  int = arbitrary+  double = arbitrary+  char = arbitrary++instance MonadRandomLike m => MonadRandomLike (RejectT m) where+  incr = RejectT $ \maxSize size retry cont ->+    if size >= maxSize then+      retry+    else+      cont (size + 1) ()+  doubleR = lift . doubleR+  integerR = lift . integerR+  int = lift int+  double = lift double+  char = lift char++instance MonadRandom m => MonadRandomLike (AMonadRandom m) where+  doubleR x = lift $ getRandomR (0, x)+  integerR x = lift $ getRandomR (0, x-1)+  int = lift getRandom+  double = lift getRandom+  char = lift getRandom
+ test/tree.hs view
@@ -0,0 +1,59 @@+{-# LANGUAGE DeriveDataTypeable #-}+import Control.Monad+import Data.Data+import Data.Foldable+import Data.List+import Test.QuickCheck+import Data.Random.Generics++data T = N T T | L+  deriving (Eq, Ord, Show, Data)++-- size+s :: T -> Int+s (N l r) = 1 + s l + s r+s L = 0++main =+  for_ [ 4 ^ e | e <- [2 .. 4] ] $ \n ->+    for_+      [ ("reject ", generatorSR)+      , ("rejectSimple ", generatorR')+      , ("point ", generatorP')+      , ("pointReject ", generatorPR')+      ] $ \(name, g) ->+      stats (name ++ show n) s (g n)++stats :: String -> (a -> Int) -> Gen a -> IO ()+stats s f g = do+  putStrLn s+  xs <- replicateM 1000 (fmap f (generate g))+  putStrLn $ "Mean: " ++ show (mean xs)+  pp (histogram xs)+  putStrLn ""++histogram xs' = (bounds, bins)+  where+    (xs, ys) = splitAt (95 * length xs' `div` 100) (sort xs')+    xMin = minimum xs+    xMax = maximum xs+    bounds+      | xMax - xMin < 20 = [xMin .. xMax]+      | otherwise = [xMin, xMin + (xMax - xMin) `div` 10 .. xMax]+    bins = f bounds xs+    f (_ : b1 : bs) xs =+      let (a, ys) = span (< b1) xs+      in length a : f (b1 : bs) ys+    f _ xs = [length xs + length ys]++pp :: ([Int], [Int]) -> IO ()+pp (vs, bs) = do+  putStrLn $ vs >>= \v -> three v ++ " - "+  putStrLn $ bs >>= \b -> " | " ++ three b++three x = replicate (3 - length s) ' ' ++ s+  where+    s = show x++mean :: Foldable v => v Int -> Double+mean xs = fromIntegral (sum xs) / fromIntegral (length xs)