Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Random instances for tuples #26

Closed
ndmitchell opened this issue Apr 23, 2015 · 25 comments
Closed

Add Random instances for tuples #26

ndmitchell opened this issue Apr 23, 2015 · 25 comments

Comments

@ndmitchell
Copy link

Looking at the interface, I see no reason tuples can't be instances of the Random class, which would make it more useful.

@cartazio
Copy link
Contributor

That sounds super reasonable. I'll talk about it with Dominic when we next
catchup.

I assume we probably don't need more Than size 6 or 10 tuples? :)

On Thursday, April 23, 2015, Neil Mitchell [email protected] wrote:

Looking at the interface, I see no reason tuples can't be instances of the
Random class, which would make it more useful.


Reply to this email directly or view it on GitHub
#26.

@ndmitchell
Copy link
Author

My particular use case was for 4 tuples - specifically, I wanted to call seedTFGen with a randomly chosen seed. Agreed, 6 seems sufficient.

rudymatela added a commit to rudymatela/random that referenced this issue Jan 3, 2017
@cartazio
Copy link
Contributor

@ndmitchell this is a good feature, i'll make it happen, see what i can do...

how would you specify/what to talk about generating samples / intervals?

@cartazio
Copy link
Contributor

(i'm amidst revisiting / generalizing the sampler api, though that wont happen for the next release, i presume you want every tuple value, not an "interval/range"?

@ndmitchell
Copy link
Author

@cartazio I didn't really have any idea about what intervals should mean - but I guess between (a,b) and (c,d) should probably be equivalent to the first member in the range a-c and the second in b-d.

@samuelpilz
Copy link

What is the status on this issue? Is there a chance that the pull-request will be merged and an update is published?

@cartazio
Copy link
Contributor

cartazio commented Apr 17, 2018 via email

@L-TChen
Copy link

L-TChen commented Jan 25, 2019

Will this be happening soon? I am really looking forward to this feature. :-)

curiousleo added a commit to curiousleo/random that referenced this issue May 26, 2020
author Alexey Kuleshevich <[email protected]> 1581472095 +0300
committer Leonhard Markert <[email protected]> 1590493894 +0200

This patch is mostly backwards compatible. See "Breaking Changes" below
for the full list of backwards incompatible changes.

This patch fixes quality and performance issues, addresses additional
miscellaneous issues, and introduces a monadic API.

Issues addressed
================

Priority issues fixed in this patch:

- Title: "The seeds generated by split are not independent"
  Link:  haskell#25
  Fixed: changed algorithm to SplitMix, which provides a robust 'split'
  operation

- Title: "Very low throughput"
  Link:  haskell#51
  Fixed: see "Performance" below

Additional issues addressed in this patch:

- Title: "Add Random instances for tuples"
  Link:  haskell#26
  Addressed: added 'Uniform' instances for up to 6-tuples

- Title: "Add Random instance for Natural"
  Link:  haskell#44
  Addressed: added 'UniformRange' instance for 'Natural'

- Title: "incorrect distribution of randomR for floating-point numbers"
  Link:  haskell#53
  Addressed: see "Regarding floating-point numbers" below

- Title: "System/Random.hs:43:1: warning: [-Wtabs]"
  Link:  haskell#55
  Fixed: no more tabs

- Title: "Why does random for Float and Double produce exactly 24 or 53 bits?"
  Link:  haskell#58
  Fixed: see "Regarding floating-point numbers" below

- Title: "read :: StdGen fails for strings longer than 6"
  Link:  haskell#59
  Addressed: 'StdGen' is no longer an instance of 'Read'

Regarding floating-point numbers: with this patch, the relevant
instances for 'Float' and 'Double' sample more bits than before but do
not sample every possible representable value. The documentation now
clearly spells out what this means for users.

Quality (issue 25)
==================

The algorithm [1] in version 1.1 of this library fails empirical PRNG
tests when used to generate "split sequences" as proposed in [3].

SplitMix [2] passes the same tests. This patch changes 'StdGen' to use
the SplitMix implementation provided by the splitmix package.

Test batteries used: dieharder, TestU1, PractRand.

[1]: P. L'Ecuyer, "Efficient and portable combined random number
generators". https://doi.org/10.1145/62959.62969

[2]: G. L. Steele, D. Lea, C. H. Flood, "Fast splittable pseudorandom
number generators". https://doi.org/10.1145/2714064.2660195

[3]: H. G. Schaathun, "Evaluation of splittable pseudo-random
generators". https://doi.org/10.1017/S095679681500012X

Performance (issue 51)
======================

The "improvement" column in the following table is a multiplier: the
improvement for 'random' for type 'Float' is 1038, so this operation is
1038 times faster with this patch.

| Name                    | Mean (1.1) | Mean (patch) | Improvement|
| ----------------------- | ---------- | ------------ | ---------- |
| pure/random/Float       |         30 |         0.03 |        1038|
| pure/random/Double      |         52 |         0.03 |        1672|
| pure/random/Integer     |         43 |         0.33 |         131|
| pure/uniform/Word8      |         14 |         0.03 |         422|
| pure/uniform/Word16     |         13 |         0.03 |         375|
| pure/uniform/Word32     |         21 |         0.03 |         594|
| pure/uniform/Word64     |         42 |         0.03 |        1283|
| pure/uniform/Word       |         44 |         0.03 |        1491|
| pure/uniform/Int8       |         15 |         0.03 |         511|
| pure/uniform/Int16      |         15 |         0.03 |         507|
| pure/uniform/Int32      |         22 |         0.03 |         749|
| pure/uniform/Int64      |         44 |         0.03 |        1405|
| pure/uniform/Int        |         43 |         0.03 |        1512|
| pure/uniform/Char       |         17 |         0.49 |          35|
| pure/uniform/Bool       |         18 |         0.03 |         618|
| pure/uniform/CChar      |         14 |         0.03 |         485|
| pure/uniform/CSChar     |         14 |         0.03 |         455|
| pure/uniform/CUChar     |         13 |         0.03 |         448|
| pure/uniform/CShort     |         14 |         0.03 |         473|
| pure/uniform/CUShort    |         13 |         0.03 |         457|
| pure/uniform/CInt       |         21 |         0.03 |         737|
| pure/uniform/CUInt      |         21 |         0.03 |         742|
| pure/uniform/CLong      |         43 |         0.03 |        1544|
| pure/uniform/CULong     |         42 |         0.03 |        1460|
| pure/uniform/CPtrdiff   |         43 |         0.03 |        1494|
| pure/uniform/CSize      |         43 |         0.03 |        1475|
| pure/uniform/CWchar     |         22 |         0.03 |         785|
| pure/uniform/CSigAtomic |         21 |         0.03 |         749|
| pure/uniform/CLLong     |         43 |         0.03 |        1554|
| pure/uniform/CULLong    |         42 |         0.03 |        1505|
| pure/uniform/CIntPtr    |         43 |         0.03 |        1476|
| pure/uniform/CUIntPtr   |         42 |         0.03 |        1463|
| pure/uniform/CIntMax    |         43 |         0.03 |        1535|
| pure/uniform/CUIntMax   |         42 |         0.03 |        1493|

API changes
===========

MonadRandom
-----------

This patch adds a class 'MonadRandom':

    -- | 'MonadRandom' is an interface to monadic pseudo-random number generators.
    class Monad m => MonadRandom g s m | g m -> s where
      {-# MINIMAL freezeGen,thawGen,(uniformWord32|uniformWord64) #-}

      type Frozen g = (f :: Type) | f -> g
      freezeGen :: g s -> m (Frozen g)
      thawGen :: Frozen g -> m (g s)

      uniformWord32 :: g s -> m Word32 -- default implementation in terms of uniformWord64
      uniformWord64 :: g s -> m Word64 -- default implementation in terms of uniformWord32
      -- plus methods for other word sizes and for byte strings
      -- all have default implementations so the MINIMAL pragma holds

Conceptually, in 'MonadRandom g s m', 'g s' is the type of the
generator, 's' is the state type, and 'm' the underlying monad. Via the
functional dependency 'g m -> s', the state type is determined by the
generator and monad.

'Frozen' is the type of the generator's state "at rest". It is defined
as an injective type family via 'f -> g', so there is no ambiguity as to
which 'g' any 'Frozen g' belongs to.

This definition is generic enough to accommodate, for example, the 'Gen'
type from the 'mwc-random' package, which itself abstracts over the
underlying primitive monad and state token. The documentation shows the
full 'MonadRandom Gen' instance.

Four 'MonadRandom' instances ("monadic adapters") are provided for pure
generators to enable their use in monadic code. The documentation
describes them in detail.

'Uniform' and 'UniformRange'
----------------------------

The 'Random' typeclass has conceptually been split into 'Uniform' and
'UniformRange'. The 'Random' typeclass is still included for backwards
compatibility. 'Uniform' is for types where it is possible to sample
from the type's entire domain; 'UniformRange' is for types where one can
sample from a specified range.

Breaking Changes
================

This patch introduces these breaking changes:

* requires 'base >= 4.10' (GHC-8.2)
* 'StdGen' is no longer an instance of 'Read'
* 'randomIO' and 'randomRIO' where extracted from the 'Random' class into
  separate functions

In addition, there may be import clashes with new functions, e.g. 'uniform' and
'uniformR'.

Deprecations
============

This patch introduces 'genWord64', 'genWord32' and similar methods to
the 'RandomGen' class. The significantly slower method 'next' and its
companion 'genRange' are now deprecated.

Co-authored-by: Alexey Kuleshevich <[email protected]>
Co-authored-by: idontgetoutmuch <[email protected]>
Co-authored-by: Leonhard Markert <[email protected]>
curiousleo added a commit to curiousleo/random that referenced this issue May 26, 2020
author Alexey Kuleshevich <[email protected]> 1581472095 +0300
committer Leonhard Markert <[email protected]> 1590493894 +0200

This patch is mostly backwards compatible. See "Breaking Changes" below
for the full list of backwards incompatible changes.

This patch fixes quality and performance issues, addresses additional
miscellaneous issues, and introduces a monadic API.

Issues addressed
================

Priority issues fixed in this patch:

- Title: "The seeds generated by split are not independent"
  Link:  haskell#25
  Fixed: changed algorithm to SplitMix, which provides a robust 'split'
  operation

- Title: "Very low throughput"
  Link:  haskell#51
  Fixed: see "Performance" below

Additional issues addressed in this patch:

- Title: "Add Random instances for tuples"
  Link:  haskell#26
  Addressed: added 'Uniform' instances for up to 6-tuples

- Title: "Add Random instance for Natural"
  Link:  haskell#44
  Addressed: added 'UniformRange' instance for 'Natural'

- Title: "incorrect distribution of randomR for floating-point numbers"
  Link:  haskell#53
  Addressed: see "Regarding floating-point numbers" below

- Title: "System/Random.hs:43:1: warning: [-Wtabs]"
  Link:  haskell#55
  Fixed: no more tabs

- Title: "Why does random for Float and Double produce exactly 24 or 53 bits?"
  Link:  haskell#58
  Fixed: see "Regarding floating-point numbers" below

- Title: "read :: StdGen fails for strings longer than 6"
  Link:  haskell#59
  Addressed: 'StdGen' is no longer an instance of 'Read'

Regarding floating-point numbers: with this patch, the relevant
instances for 'Float' and 'Double' sample more bits than before but do
not sample every possible representable value. The documentation now
clearly spells out what this means for users.

Quality (issue 25)
==================

The algorithm [1] in version 1.1 of this library fails empirical PRNG
tests when used to generate "split sequences" as proposed in [3].

SplitMix [2] passes the same tests. This patch changes 'StdGen' to use
the SplitMix implementation provided by the splitmix package.

Test batteries used: dieharder, TestU1, PractRand.

[1]: P. L'Ecuyer, "Efficient and portable combined random number
generators". https://doi.org/10.1145/62959.62969

[2]: G. L. Steele, D. Lea, C. H. Flood, "Fast splittable pseudorandom
number generators". https://doi.org/10.1145/2714064.2660195

[3]: H. G. Schaathun, "Evaluation of splittable pseudo-random
generators". https://doi.org/10.1017/S095679681500012X

Performance (issue 51)
======================

The "improvement" column in the following table is a multiplier: the
improvement for 'random' for type 'Float' is 1038, so this operation is
1038 times faster with this patch.

| Name                    | Mean (1.1) | Mean (patch) | Improvement|
| ----------------------- | ---------- | ------------ | ---------- |
| pure/random/Float       |         30 |         0.03 |        1038|
| pure/random/Double      |         52 |         0.03 |        1672|
| pure/random/Integer     |         43 |         0.33 |         131|
| pure/uniform/Word8      |         14 |         0.03 |         422|
| pure/uniform/Word16     |         13 |         0.03 |         375|
| pure/uniform/Word32     |         21 |         0.03 |         594|
| pure/uniform/Word64     |         42 |         0.03 |        1283|
| pure/uniform/Word       |         44 |         0.03 |        1491|
| pure/uniform/Int8       |         15 |         0.03 |         511|
| pure/uniform/Int16      |         15 |         0.03 |         507|
| pure/uniform/Int32      |         22 |         0.03 |         749|
| pure/uniform/Int64      |         44 |         0.03 |        1405|
| pure/uniform/Int        |         43 |         0.03 |        1512|
| pure/uniform/Char       |         17 |         0.49 |          35|
| pure/uniform/Bool       |         18 |         0.03 |         618|
| pure/uniform/CChar      |         14 |         0.03 |         485|
| pure/uniform/CSChar     |         14 |         0.03 |         455|
| pure/uniform/CUChar     |         13 |         0.03 |         448|
| pure/uniform/CShort     |         14 |         0.03 |         473|
| pure/uniform/CUShort    |         13 |         0.03 |         457|
| pure/uniform/CInt       |         21 |         0.03 |         737|
| pure/uniform/CUInt      |         21 |         0.03 |         742|
| pure/uniform/CLong      |         43 |         0.03 |        1544|
| pure/uniform/CULong     |         42 |         0.03 |        1460|
| pure/uniform/CPtrdiff   |         43 |         0.03 |        1494|
| pure/uniform/CSize      |         43 |         0.03 |        1475|
| pure/uniform/CWchar     |         22 |         0.03 |         785|
| pure/uniform/CSigAtomic |         21 |         0.03 |         749|
| pure/uniform/CLLong     |         43 |         0.03 |        1554|
| pure/uniform/CULLong    |         42 |         0.03 |        1505|
| pure/uniform/CIntPtr    |         43 |         0.03 |        1476|
| pure/uniform/CUIntPtr   |         42 |         0.03 |        1463|
| pure/uniform/CIntMax    |         43 |         0.03 |        1535|
| pure/uniform/CUIntMax   |         42 |         0.03 |        1493|

API changes
===========

MonadRandom
-----------

This patch adds a class 'MonadRandom':

    -- | 'MonadRandom' is an interface to monadic pseudo-random number generators.
    class Monad m => MonadRandom g s m | g m -> s where
      {-# MINIMAL freezeGen,thawGen,(uniformWord32|uniformWord64) #-}

      type Frozen g = (f :: Type) | f -> g
      freezeGen :: g s -> m (Frozen g)
      thawGen :: Frozen g -> m (g s)

      uniformWord32 :: g s -> m Word32 -- default implementation in terms of uniformWord64
      uniformWord64 :: g s -> m Word64 -- default implementation in terms of uniformWord32
      -- plus methods for other word sizes and for byte strings
      -- all have default implementations so the MINIMAL pragma holds

Conceptually, in 'MonadRandom g s m', 'g s' is the type of the
generator, 's' is the state type, and 'm' the underlying monad. Via the
functional dependency 'g m -> s', the state type is determined by the
generator and monad.

'Frozen' is the type of the generator's state "at rest". It is defined
as an injective type family via 'f -> g', so there is no ambiguity as to
which 'g' any 'Frozen g' belongs to.

This definition is generic enough to accommodate, for example, the 'Gen'
type from the 'mwc-random' package, which itself abstracts over the
underlying primitive monad and state token. The documentation shows the
full 'MonadRandom Gen' instance.

Four 'MonadRandom' instances ("monadic adapters") are provided for pure
generators to enable their use in monadic code. The documentation
describes them in detail.

'Uniform' and 'UniformRange'
----------------------------

The 'Random' typeclass has conceptually been split into 'Uniform' and
'UniformRange'. The 'Random' typeclass is still included for backwards
compatibility. 'Uniform' is for types where it is possible to sample
from the type's entire domain; 'UniformRange' is for types where one can
sample from a specified range.

Breaking Changes
================

This patch introduces these breaking changes:

* requires 'base >= 4.10' (GHC-8.2)
* 'StdGen' is no longer an instance of 'Read'
* 'randomIO' and 'randomRIO' where extracted from the 'Random' class into
  separate functions

In addition, there may be import clashes with new functions, e.g. 'uniform' and
'uniformR'.

Deprecations
============

This patch introduces 'genWord64', 'genWord32' and similar methods to
the 'RandomGen' class. The significantly slower method 'next' and its
companion 'genRange' are now deprecated.

Co-authored-by: Alexey Kuleshevich <[email protected]>
Co-authored-by: idontgetoutmuch <[email protected]>
Co-authored-by: Leonhard Markert <[email protected]>
curiousleo added a commit to curiousleo/random that referenced this issue May 26, 2020
This patch is mostly backwards compatible. See "Breaking Changes" below
for the full list of backwards incompatible changes.

This patch fixes quality and performance issues, addresses additional
miscellaneous issues, and introduces a monadic API.

Issues addressed
================

Priority issues fixed in this patch:

- Title: "The seeds generated by split are not independent"
  Link:  haskell#25
  Fixed: changed algorithm to SplitMix, which provides a robust 'split'
  operation

- Title: "Very low throughput"
  Link:  haskell#51
  Fixed: see "Performance" below

Additional issues addressed in this patch:

- Title: "Add Random instances for tuples"
  Link:  haskell#26
  Addressed: added 'Uniform' instances for up to 6-tuples

- Title: "Add Random instance for Natural"
  Link:  haskell#44
  Addressed: added 'UniformRange' instance for 'Natural'

- Title: "incorrect distribution of randomR for floating-point numbers"
  Link:  haskell#53
  Addressed: see "Regarding floating-point numbers" below

- Title: "System/Random.hs:43:1: warning: [-Wtabs]"
  Link:  haskell#55
  Fixed: no more tabs

- Title: "Why does random for Float and Double produce exactly 24 or 53 bits?"
  Link:  haskell#58
  Fixed: see "Regarding floating-point numbers" below

- Title: "read :: StdGen fails for strings longer than 6"
  Link:  haskell#59
  Addressed: 'StdGen' is no longer an instance of 'Read'

Regarding floating-point numbers: with this patch, the relevant
instances for 'Float' and 'Double' sample more bits than before but do
not sample every possible representable value. The documentation now
clearly spells out what this means for users.

Quality (issue 25)
==================

The algorithm [1] in version 1.1 of this library fails empirical PRNG
tests when used to generate "split sequences" as proposed in [3].

SplitMix [2] passes the same tests. This patch changes 'StdGen' to use
the SplitMix implementation provided by the splitmix package.

Test batteries used: dieharder, TestU1, PractRand.

[1]: P. L'Ecuyer, "Efficient and portable combined random number
generators". https://doi.org/10.1145/62959.62969

[2]: G. L. Steele, D. Lea, C. H. Flood, "Fast splittable pseudorandom
number generators". https://doi.org/10.1145/2714064.2660195

[3]: H. G. Schaathun, "Evaluation of splittable pseudo-random
generators". https://doi.org/10.1017/S095679681500012X

Performance (issue 51)
======================

The "improvement" column in the following table is a multiplier: the
improvement for 'random' for type 'Float' is 1038, so this operation is
1038 times faster with this patch.

| Name                    | Mean (1.1) | Mean (patch) | Improvement|
| ----------------------- | ---------- | ------------ | ---------- |
| pure/random/Float       |         30 |         0.03 |        1038|
| pure/random/Double      |         52 |         0.03 |        1672|
| pure/random/Integer     |         43 |         0.33 |         131|
| pure/uniform/Word8      |         14 |         0.03 |         422|
| pure/uniform/Word16     |         13 |         0.03 |         375|
| pure/uniform/Word32     |         21 |         0.03 |         594|
| pure/uniform/Word64     |         42 |         0.03 |        1283|
| pure/uniform/Word       |         44 |         0.03 |        1491|
| pure/uniform/Int8       |         15 |         0.03 |         511|
| pure/uniform/Int16      |         15 |         0.03 |         507|
| pure/uniform/Int32      |         22 |         0.03 |         749|
| pure/uniform/Int64      |         44 |         0.03 |        1405|
| pure/uniform/Int        |         43 |         0.03 |        1512|
| pure/uniform/Char       |         17 |         0.49 |          35|
| pure/uniform/Bool       |         18 |         0.03 |         618|
| pure/uniform/CChar      |         14 |         0.03 |         485|
| pure/uniform/CSChar     |         14 |         0.03 |         455|
| pure/uniform/CUChar     |         13 |         0.03 |         448|
| pure/uniform/CShort     |         14 |         0.03 |         473|
| pure/uniform/CUShort    |         13 |         0.03 |         457|
| pure/uniform/CInt       |         21 |         0.03 |         737|
| pure/uniform/CUInt      |         21 |         0.03 |         742|
| pure/uniform/CLong      |         43 |         0.03 |        1544|
| pure/uniform/CULong     |         42 |         0.03 |        1460|
| pure/uniform/CPtrdiff   |         43 |         0.03 |        1494|
| pure/uniform/CSize      |         43 |         0.03 |        1475|
| pure/uniform/CWchar     |         22 |         0.03 |         785|
| pure/uniform/CSigAtomic |         21 |         0.03 |         749|
| pure/uniform/CLLong     |         43 |         0.03 |        1554|
| pure/uniform/CULLong    |         42 |         0.03 |        1505|
| pure/uniform/CIntPtr    |         43 |         0.03 |        1476|
| pure/uniform/CUIntPtr   |         42 |         0.03 |        1463|
| pure/uniform/CIntMax    |         43 |         0.03 |        1535|
| pure/uniform/CUIntMax   |         42 |         0.03 |        1493|

API changes
===========

MonadRandom
-----------

This patch adds a class 'MonadRandom':

    -- | 'MonadRandom' is an interface to monadic pseudo-random number generators.
    class Monad m => MonadRandom g s m | g m -> s where
      {-# MINIMAL freezeGen,thawGen,(uniformWord32|uniformWord64) #-}

      type Frozen g = (f :: Type) | f -> g
      freezeGen :: g s -> m (Frozen g)
      thawGen :: Frozen g -> m (g s)

      uniformWord32 :: g s -> m Word32 -- default implementation in terms of uniformWord64
      uniformWord64 :: g s -> m Word64 -- default implementation in terms of uniformWord32
      -- plus methods for other word sizes and for byte strings
      -- all have default implementations so the MINIMAL pragma holds

Conceptually, in 'MonadRandom g s m', 'g s' is the type of the
generator, 's' is the state type, and 'm' the underlying monad. Via the
functional dependency 'g m -> s', the state type is determined by the
generator and monad.

'Frozen' is the type of the generator's state "at rest". It is defined
as an injective type family via 'f -> g', so there is no ambiguity as to
which 'g' any 'Frozen g' belongs to.

This definition is generic enough to accommodate, for example, the 'Gen'
type from the 'mwc-random' package, which itself abstracts over the
underlying primitive monad and state token. The documentation shows the
full 'MonadRandom Gen' instance.

Four 'MonadRandom' instances ("monadic adapters") are provided for pure
generators to enable their use in monadic code. The documentation
describes them in detail.

'Uniform' and 'UniformRange'
----------------------------

The 'Random' typeclass has conceptually been split into 'Uniform' and
'UniformRange'. The 'Random' typeclass is still included for backwards
compatibility. 'Uniform' is for types where it is possible to sample
from the type's entire domain; 'UniformRange' is for types where one can
sample from a specified range.

Breaking Changes
================

This patch introduces these breaking changes:

* requires 'base >= 4.10' (GHC-8.2)
* 'StdGen' is no longer an instance of 'Read'
* 'randomIO' and 'randomRIO' where extracted from the 'Random' class into
  separate functions

In addition, there may be import clashes with new functions, e.g. 'uniform' and
'uniformR'.

Deprecations
============

This patch introduces 'genWord64', 'genWord32' and similar methods to
the 'RandomGen' class. The significantly slower method 'next' and its
companion 'genRange' are now deprecated.

Co-authored-by: Alexey Kuleshevich <[email protected]>
Co-authored-by: idontgetoutmuch <[email protected]>
Co-authored-by: Leonhard Markert <[email protected]>
lehins added a commit to idontgetoutmuch/random that referenced this issue May 27, 2020
This patch is mostly backwards compatible. See "Breaking Changes" below
for the full list of backwards incompatible changes.

This patch fixes quality and performance issues, addresses additional
miscellaneous issues, and introduces a monadic API.

Issues addressed
================

Priority issues fixed in this patch:

- Title: "The seeds generated by split are not independent"
  Link:  haskell#25
  Fixed: changed algorithm to SplitMix, which provides a robust 'split'
  operation

- Title: "Very low throughput"
  Link:  haskell#51
  Fixed: see "Performance" below

Additional issues addressed in this patch:

- Title: "Add Random instances for tuples"
  Link:  haskell#26
  Addressed: added 'Uniform' instances for up to 6-tuples

- Title: "Add Random instance for Natural"
  Link:  haskell#44
  Addressed: added 'UniformRange' instance for 'Natural'

- Title: "incorrect distribution of randomR for floating-point numbers"
  Link:  haskell#53
  Addressed: see "Regarding floating-point numbers" below

- Title: "System/Random.hs:43:1: warning: [-Wtabs]"
  Link:  haskell#55
  Fixed: no more tabs

- Title: "Why does random for Float and Double produce exactly 24 or 53 bits?"
  Link:  haskell#58
  Fixed: see "Regarding floating-point numbers" below

- Title: "read :: StdGen fails for strings longer than 6"
  Link:  haskell#59
  Addressed: 'StdGen' is no longer an instance of 'Read'

Regarding floating-point numbers: with this patch, the relevant
instances for 'Float' and 'Double' sample more bits than before but do
not sample every possible representable value. The documentation now
clearly spells out what this means for users.

Quality (issue 25)
==================

The algorithm [1] in version 1.1 of this library fails empirical PRNG
tests when used to generate "split sequences" as proposed in [3].

SplitMix [2] passes the same tests. This patch changes 'StdGen' to use
the SplitMix implementation provided by the splitmix package.

Test batteries used: dieharder, TestU1, PractRand.

[1]: P. L'Ecuyer, "Efficient and portable combined random number
generators". https://doi.org/10.1145/62959.62969

[2]: G. L. Steele, D. Lea, C. H. Flood, "Fast splittable pseudorandom
number generators". https://doi.org/10.1145/2714064.2660195

[3]: H. G. Schaathun, "Evaluation of splittable pseudo-random
generators". https://doi.org/10.1017/S095679681500012X

Performance (issue 51)
======================

The "improvement" column in the following table is a multiplier: the
improvement for 'random' for type 'Float' is 1038, so this operation is
1038 times faster with this patch.

| Name                    | Mean (1.1) | Mean (patch) | Improvement|
| ----------------------- | ---------- | ------------ | ---------- |
| pure/random/Float       |         30 |         0.03 |        1038|
| pure/random/Double      |         52 |         0.03 |        1672|
| pure/random/Integer     |         43 |         0.33 |         131|
| pure/uniform/Word8      |         14 |         0.03 |         422|
| pure/uniform/Word16     |         13 |         0.03 |         375|
| pure/uniform/Word32     |         21 |         0.03 |         594|
| pure/uniform/Word64     |         42 |         0.03 |        1283|
| pure/uniform/Word       |         44 |         0.03 |        1491|
| pure/uniform/Int8       |         15 |         0.03 |         511|
| pure/uniform/Int16      |         15 |         0.03 |         507|
| pure/uniform/Int32      |         22 |         0.03 |         749|
| pure/uniform/Int64      |         44 |         0.03 |        1405|
| pure/uniform/Int        |         43 |         0.03 |        1512|
| pure/uniform/Char       |         17 |         0.49 |          35|
| pure/uniform/Bool       |         18 |         0.03 |         618|
| pure/uniform/CChar      |         14 |         0.03 |         485|
| pure/uniform/CSChar     |         14 |         0.03 |         455|
| pure/uniform/CUChar     |         13 |         0.03 |         448|
| pure/uniform/CShort     |         14 |         0.03 |         473|
| pure/uniform/CUShort    |         13 |         0.03 |         457|
| pure/uniform/CInt       |         21 |         0.03 |         737|
| pure/uniform/CUInt      |         21 |         0.03 |         742|
| pure/uniform/CLong      |         43 |         0.03 |        1544|
| pure/uniform/CULong     |         42 |         0.03 |        1460|
| pure/uniform/CPtrdiff   |         43 |         0.03 |        1494|
| pure/uniform/CSize      |         43 |         0.03 |        1475|
| pure/uniform/CWchar     |         22 |         0.03 |         785|
| pure/uniform/CSigAtomic |         21 |         0.03 |         749|
| pure/uniform/CLLong     |         43 |         0.03 |        1554|
| pure/uniform/CULLong    |         42 |         0.03 |        1505|
| pure/uniform/CIntPtr    |         43 |         0.03 |        1476|
| pure/uniform/CUIntPtr   |         42 |         0.03 |        1463|
| pure/uniform/CIntMax    |         43 |         0.03 |        1535|
| pure/uniform/CUIntMax   |         42 |         0.03 |        1493|

API changes
===========

MonadRandom
-----------

This patch adds a class 'MonadRandom':

    -- | 'MonadRandom' is an interface to monadic pseudo-random number generators.
    class Monad m => MonadRandom g s m | g m -> s where
      {-# MINIMAL freezeGen,thawGen,(uniformWord32|uniformWord64) #-}

      type Frozen g = (f :: Type) | f -> g
      freezeGen :: g s -> m (Frozen g)
      thawGen :: Frozen g -> m (g s)

      uniformWord32 :: g s -> m Word32 -- default implementation in terms of uniformWord64
      uniformWord64 :: g s -> m Word64 -- default implementation in terms of uniformWord32
      -- plus methods for other word sizes and for byte strings
      -- all have default implementations so the MINIMAL pragma holds

Conceptually, in 'MonadRandom g s m', 'g s' is the type of the
generator, 's' is the state type, and 'm' the underlying monad. Via the
functional dependency 'g m -> s', the state type is determined by the
generator and monad.

'Frozen' is the type of the generator's state "at rest". It is defined
as an injective type family via 'f -> g', so there is no ambiguity as to
which 'g' any 'Frozen g' belongs to.

This definition is generic enough to accommodate, for example, the 'Gen'
type from the 'mwc-random' package, which itself abstracts over the
underlying primitive monad and state token. The documentation shows the
full 'MonadRandom Gen' instance.

Four 'MonadRandom' instances ("monadic adapters") are provided for pure
generators to enable their use in monadic code. The documentation
describes them in detail.

'Uniform' and 'UniformRange'
----------------------------

The 'Random' typeclass has conceptually been split into 'Uniform' and
'UniformRange'. The 'Random' typeclass is still included for backwards
compatibility. 'Uniform' is for types where it is possible to sample
from the type's entire domain; 'UniformRange' is for types where one can
sample from a specified range.

Breaking Changes
================

This patch introduces these breaking changes:

* requires 'base >= 4.10' (GHC-8.2)
* 'StdGen' is no longer an instance of 'Read'
* 'randomIO' and 'randomRIO' where extracted from the 'Random' class into
  separate functions

In addition, there may be import clashes with new functions, e.g. 'uniform' and
'uniformR'.

Deprecations
============

This patch introduces 'genWord64', 'genWord32' and similar methods to
the 'RandomGen' class. The significantly slower method 'next' and its
companion 'genRange' are now deprecated.

Co-authored-by: Alexey Kuleshevich <[email protected]>
Co-authored-by: idontgetoutmuch <[email protected]>
Co-authored-by: Leonhard Markert <[email protected]>
lehins added a commit to idontgetoutmuch/random that referenced this issue May 27, 2020
This patch is mostly backwards compatible. See "Breaking Changes" below
for the full list of backwards incompatible changes.

This patch fixes quality and performance issues, addresses additional
miscellaneous issues, and introduces a monadic API.

Issues addressed
================

Priority issues fixed in this patch:

- Title: "The seeds generated by split are not independent"
  Link:  haskell#25
  Fixed: changed algorithm to SplitMix, which provides a robust 'split'
  operation

- Title: "Very low throughput"
  Link:  haskell#51
  Fixed: see "Performance" below

Additional issues addressed in this patch:

- Title: "Add Random instances for tuples"
  Link:  haskell#26
  Addressed: added 'Uniform' instances for up to 6-tuples

- Title: "Add Random instance for Natural"
  Link:  haskell#44
  Addressed: added 'UniformRange' instance for 'Natural'

- Title: "incorrect distribution of randomR for floating-point numbers"
  Link:  haskell#53
  Addressed: see "Regarding floating-point numbers" below

- Title: "System/Random.hs:43:1: warning: [-Wtabs]"
  Link:  haskell#55
  Fixed: no more tabs

- Title: "Why does random for Float and Double produce exactly 24 or 53 bits?"
  Link:  haskell#58
  Fixed: see "Regarding floating-point numbers" below

- Title: "read :: StdGen fails for strings longer than 6"
  Link:  haskell#59
  Addressed: 'StdGen' is no longer an instance of 'Read'

Regarding floating-point numbers: with this patch, the relevant
instances for 'Float' and 'Double' sample more bits than before but do
not sample every possible representable value. The documentation now
clearly spells out what this means for users.

Quality (issue 25)
==================

The algorithm [1] in version 1.1 of this library fails empirical PRNG
tests when used to generate "split sequences" as proposed in [3].

SplitMix [2] passes the same tests. This patch changes 'StdGen' to use
the SplitMix implementation provided by the splitmix package.

Test batteries used: dieharder, TestU1, PractRand.

[1]: P. L'Ecuyer, "Efficient and portable combined random number
generators". https://doi.org/10.1145/62959.62969

[2]: G. L. Steele, D. Lea, C. H. Flood, "Fast splittable pseudorandom
number generators". https://doi.org/10.1145/2714064.2660195

[3]: H. G. Schaathun, "Evaluation of splittable pseudo-random
generators". https://doi.org/10.1017/S095679681500012X

Performance (issue 51)
======================

The "improvement" column in the following table is a multiplier: the
improvement for 'random' for type 'Float' is 1038, so this operation is
1038 times faster with this patch.

| Name                    | Mean (1.1) | Mean (patch) | Improvement|
| ----------------------- | ---------- | ------------ | ---------- |
| pure/random/Float       |         30 |         0.03 |        1038|
| pure/random/Double      |         52 |         0.03 |        1672|
| pure/random/Integer     |         43 |         0.33 |         131|
| pure/uniform/Word8      |         14 |         0.03 |         422|
| pure/uniform/Word16     |         13 |         0.03 |         375|
| pure/uniform/Word32     |         21 |         0.03 |         594|
| pure/uniform/Word64     |         42 |         0.03 |        1283|
| pure/uniform/Word       |         44 |         0.03 |        1491|
| pure/uniform/Int8       |         15 |         0.03 |         511|
| pure/uniform/Int16      |         15 |         0.03 |         507|
| pure/uniform/Int32      |         22 |         0.03 |         749|
| pure/uniform/Int64      |         44 |         0.03 |        1405|
| pure/uniform/Int        |         43 |         0.03 |        1512|
| pure/uniform/Char       |         17 |         0.49 |          35|
| pure/uniform/Bool       |         18 |         0.03 |         618|
| pure/uniform/CChar      |         14 |         0.03 |         485|
| pure/uniform/CSChar     |         14 |         0.03 |         455|
| pure/uniform/CUChar     |         13 |         0.03 |         448|
| pure/uniform/CShort     |         14 |         0.03 |         473|
| pure/uniform/CUShort    |         13 |         0.03 |         457|
| pure/uniform/CInt       |         21 |         0.03 |         737|
| pure/uniform/CUInt      |         21 |         0.03 |         742|
| pure/uniform/CLong      |         43 |         0.03 |        1544|
| pure/uniform/CULong     |         42 |         0.03 |        1460|
| pure/uniform/CPtrdiff   |         43 |         0.03 |        1494|
| pure/uniform/CSize      |         43 |         0.03 |        1475|
| pure/uniform/CWchar     |         22 |         0.03 |         785|
| pure/uniform/CSigAtomic |         21 |         0.03 |         749|
| pure/uniform/CLLong     |         43 |         0.03 |        1554|
| pure/uniform/CULLong    |         42 |         0.03 |        1505|
| pure/uniform/CIntPtr    |         43 |         0.03 |        1476|
| pure/uniform/CUIntPtr   |         42 |         0.03 |        1463|
| pure/uniform/CIntMax    |         43 |         0.03 |        1535|
| pure/uniform/CUIntMax   |         42 |         0.03 |        1493|

API changes
===========

MonadRandom
-----------

This patch adds a class 'MonadRandom':

    -- | 'MonadRandom' is an interface to monadic pseudo-random number generators.
    class Monad m => MonadRandom g s m | g m -> s where
      {-# MINIMAL freezeGen,thawGen,(uniformWord32|uniformWord64) #-}

      type Frozen g = (f :: Type) | f -> g
      freezeGen :: g s -> m (Frozen g)
      thawGen :: Frozen g -> m (g s)

      uniformWord32 :: g s -> m Word32 -- default implementation in terms of uniformWord64
      uniformWord64 :: g s -> m Word64 -- default implementation in terms of uniformWord32
      -- plus methods for other word sizes and for byte strings
      -- all have default implementations so the MINIMAL pragma holds

Conceptually, in 'MonadRandom g s m', 'g s' is the type of the
generator, 's' is the state type, and 'm' the underlying monad. Via the
functional dependency 'g m -> s', the state type is determined by the
generator and monad.

'Frozen' is the type of the generator's state "at rest". It is defined
as an injective type family via 'f -> g', so there is no ambiguity as to
which 'g' any 'Frozen g' belongs to.

This definition is generic enough to accommodate, for example, the 'Gen'
type from the 'mwc-random' package, which itself abstracts over the
underlying primitive monad and state token. The documentation shows the
full 'MonadRandom Gen' instance.

Four 'MonadRandom' instances ("monadic adapters") are provided for pure
generators to enable their use in monadic code. The documentation
describes them in detail.

'Uniform' and 'UniformRange'
----------------------------

The 'Random' typeclass has conceptually been split into 'Uniform' and
'UniformRange'. The 'Random' typeclass is still included for backwards
compatibility. 'Uniform' is for types where it is possible to sample
from the type's entire domain; 'UniformRange' is for types where one can
sample from a specified range.

Breaking Changes
================

This patch introduces these breaking changes:

* requires 'base >= 4.10' (GHC-8.2)
* 'StdGen' is no longer an instance of 'Read'
* 'randomIO' and 'randomRIO' where extracted from the 'Random' class into
  separate functions

In addition, there may be import clashes with new functions, e.g. 'uniform' and
'uniformR'.

Deprecations
============

This patch introduces 'genWord64', 'genWord32' and similar methods to
the 'RandomGen' class. The significantly slower method 'next' and its
companion 'genRange' are now deprecated.

Co-authored-by: Alexey Kuleshevich <[email protected]>
Co-authored-by: idontgetoutmuch <[email protected]>
Co-authored-by: Leonhard Markert <[email protected]>
lehins added a commit to idontgetoutmuch/random that referenced this issue May 27, 2020
This patch is mostly backwards compatible. See "Breaking Changes" below
for the full list of backwards incompatible changes.

This patch fixes quality and performance issues, addresses additional
miscellaneous issues, and introduces a monadic API.

Issues addressed
================

Priority issues fixed in this patch:

- Title: "The seeds generated by split are not independent"
  Link:  haskell#25
  Fixed: changed algorithm to SplitMix, which provides a robust 'split'
  operation

- Title: "Very low throughput"
  Link:  haskell#51
  Fixed: see "Performance" below

Additional issues addressed in this patch:

- Title: "Add Random instances for tuples"
  Link:  haskell#26
  Addressed: added 'Uniform' instances for up to 6-tuples

- Title: "Add Random instance for Natural"
  Link:  haskell#44
  Addressed: added 'UniformRange' instance for 'Natural'

- Title: "incorrect distribution of randomR for floating-point numbers"
  Link:  haskell#53
  Addressed: see "Regarding floating-point numbers" below

- Title: "System/Random.hs:43:1: warning: [-Wtabs]"
  Link:  haskell#55
  Fixed: no more tabs

- Title: "Why does random for Float and Double produce exactly 24 or 53 bits?"
  Link:  haskell#58
  Fixed: see "Regarding floating-point numbers" below

- Title: "read :: StdGen fails for strings longer than 6"
  Link:  haskell#59
  Addressed: 'StdGen' is no longer an instance of 'Read'

Regarding floating-point numbers: with this patch, the relevant
instances for 'Float' and 'Double' sample more bits than before but do
not sample every possible representable value. The documentation now
clearly spells out what this means for users.

Quality (issue 25)
==================

The algorithm [1] in version 1.1 of this library fails empirical PRNG
tests when used to generate "split sequences" as proposed in [3].

SplitMix [2] passes the same tests. This patch changes 'StdGen' to use
the SplitMix implementation provided by the splitmix package.

Test batteries used: dieharder, TestU1, PractRand.

[1]: P. L'Ecuyer, "Efficient and portable combined random number
generators". https://doi.org/10.1145/62959.62969

[2]: G. L. Steele, D. Lea, C. H. Flood, "Fast splittable pseudorandom
number generators". https://doi.org/10.1145/2714064.2660195

[3]: H. G. Schaathun, "Evaluation of splittable pseudo-random
generators". https://doi.org/10.1017/S095679681500012X

Performance (issue 51)
======================

The "improvement" column in the following table is a multiplier: the
improvement for 'random' for type 'Float' is 1038, so this operation is
1038 times faster with this patch.

| Name                    | Mean (1.1) | Mean (patch) | Improvement|
| ----------------------- | ---------- | ------------ | ---------- |
| pure/random/Float       |         30 |         0.03 |        1038|
| pure/random/Double      |         52 |         0.03 |        1672|
| pure/random/Integer     |         43 |         0.33 |         131|
| pure/uniform/Word8      |         14 |         0.03 |         422|
| pure/uniform/Word16     |         13 |         0.03 |         375|
| pure/uniform/Word32     |         21 |         0.03 |         594|
| pure/uniform/Word64     |         42 |         0.03 |        1283|
| pure/uniform/Word       |         44 |         0.03 |        1491|
| pure/uniform/Int8       |         15 |         0.03 |         511|
| pure/uniform/Int16      |         15 |         0.03 |         507|
| pure/uniform/Int32      |         22 |         0.03 |         749|
| pure/uniform/Int64      |         44 |         0.03 |        1405|
| pure/uniform/Int        |         43 |         0.03 |        1512|
| pure/uniform/Char       |         17 |         0.49 |          35|
| pure/uniform/Bool       |         18 |         0.03 |         618|
| pure/uniform/CChar      |         14 |         0.03 |         485|
| pure/uniform/CSChar     |         14 |         0.03 |         455|
| pure/uniform/CUChar     |         13 |         0.03 |         448|
| pure/uniform/CShort     |         14 |         0.03 |         473|
| pure/uniform/CUShort    |         13 |         0.03 |         457|
| pure/uniform/CInt       |         21 |         0.03 |         737|
| pure/uniform/CUInt      |         21 |         0.03 |         742|
| pure/uniform/CLong      |         43 |         0.03 |        1544|
| pure/uniform/CULong     |         42 |         0.03 |        1460|
| pure/uniform/CPtrdiff   |         43 |         0.03 |        1494|
| pure/uniform/CSize      |         43 |         0.03 |        1475|
| pure/uniform/CWchar     |         22 |         0.03 |         785|
| pure/uniform/CSigAtomic |         21 |         0.03 |         749|
| pure/uniform/CLLong     |         43 |         0.03 |        1554|
| pure/uniform/CULLong    |         42 |         0.03 |        1505|
| pure/uniform/CIntPtr    |         43 |         0.03 |        1476|
| pure/uniform/CUIntPtr   |         42 |         0.03 |        1463|
| pure/uniform/CIntMax    |         43 |         0.03 |        1535|
| pure/uniform/CUIntMax   |         42 |         0.03 |        1493|

API changes
===========

MonadRandom
-----------

This patch adds a class 'MonadRandom':

    -- | 'MonadRandom' is an interface to monadic pseudo-random number generators.
    class Monad m => MonadRandom g s m | g m -> s where
      {-# MINIMAL freezeGen,thawGen,(uniformWord32|uniformWord64) #-}

      type Frozen g = (f :: Type) | f -> g
      freezeGen :: g s -> m (Frozen g)
      thawGen :: Frozen g -> m (g s)

      uniformWord32 :: g s -> m Word32 -- default implementation in terms of uniformWord64
      uniformWord64 :: g s -> m Word64 -- default implementation in terms of uniformWord32
      -- plus methods for other word sizes and for byte strings
      -- all have default implementations so the MINIMAL pragma holds

Conceptually, in 'MonadRandom g s m', 'g s' is the type of the
generator, 's' is the state type, and 'm' the underlying monad. Via the
functional dependency 'g m -> s', the state type is determined by the
generator and monad.

'Frozen' is the type of the generator's state "at rest". It is defined
as an injective type family via 'f -> g', so there is no ambiguity as to
which 'g' any 'Frozen g' belongs to.

This definition is generic enough to accommodate, for example, the 'Gen'
type from the 'mwc-random' package, which itself abstracts over the
underlying primitive monad and state token. The documentation shows the
full 'MonadRandom Gen' instance.

Four 'MonadRandom' instances ("monadic adapters") are provided for pure
generators to enable their use in monadic code. The documentation
describes them in detail.

'Uniform' and 'UniformRange'
----------------------------

The 'Random' typeclass has conceptually been split into 'Uniform' and
'UniformRange'. The 'Random' typeclass is still included for backwards
compatibility. 'Uniform' is for types where it is possible to sample
from the type's entire domain; 'UniformRange' is for types where one can
sample from a specified range.

Breaking Changes
================

This patch introduces these breaking changes:

* requires 'base >= 4.10' (GHC-8.2)
* 'StdGen' is no longer an instance of 'Read'
* 'randomIO' and 'randomRIO' where extracted from the 'Random' class into
  separate functions

In addition, there may be import clashes with new functions, e.g. 'uniform' and
'uniformR'.

Deprecations
============

This patch introduces 'genWord64', 'genWord32' and similar methods to
the 'RandomGen' class. The significantly slower method 'next' and its
companion 'genRange' are now deprecated.

Co-authored-by: Alexey Kuleshevich <[email protected]>
Co-authored-by: idontgetoutmuch <[email protected]>
Co-authored-by: Leonhard Markert <[email protected]>
@lehins lehins mentioned this issue Jun 4, 2020
curiousleo added a commit to curiousleo/random that referenced this issue Jun 15, 2020
This patch is mostly backwards compatible. See "Breaking Changes" below
for the full list of backwards incompatible changes.

This patch fixes quality and performance issues, addresses additional
miscellaneous issues, and introduces a monadic API.

Issues addressed
================

Priority issues fixed in this patch:

- Title: "The seeds generated by split are not independent"
  Link:  haskell#25
  Fixed: changed algorithm to SplitMix, which provides a robust 'split'
  operation

- Title: "Very low throughput"
  Link:  haskell#51
  Fixed: see "Performance" below

Additional issues addressed in this patch:

- Title: "Add Random instances for tuples"
  Link:  haskell#26
  Addressed: added 'Uniform' instances for up to 6-tuples

- Title: "Add Random instance for Natural"
  Link:  haskell#44
  Addressed: added 'UniformRange' instance for 'Natural'

- Title: "incorrect distribution of randomR for floating-point numbers"
  Link:  haskell#53
  Addressed: see "Regarding floating-point numbers" below

- Title: "System/Random.hs:43:1: warning: [-Wtabs]"
  Link:  haskell#55
  Fixed: no more tabs

- Title: "Why does random for Float and Double produce exactly 24 or 53 bits?"
  Link:  haskell#58
  Fixed: see "Regarding floating-point numbers" below

- Title: "read :: StdGen fails for strings longer than 6"
  Link:  haskell#59
  Addressed: 'StdGen' is no longer an instance of 'Read'

Regarding floating-point numbers: with this patch, the relevant
instances for 'Float' and 'Double' sample more bits than before but do
not sample every possible representable value. The documentation now
clearly spells out what this means for users.

Quality (issue 25)
==================

The algorithm [1] in version 1.1 of this library fails empirical PRNG
tests when used to generate "split sequences" as proposed in [3].

SplitMix [2] passes the same tests. This patch changes 'StdGen' to use
the SplitMix implementation provided by the splitmix package.

Test batteries used: dieharder, TestU1, PractRand.

[1]: P. L'Ecuyer, "Efficient and portable combined random number
generators". https://doi.org/10.1145/62959.62969

[2]: G. L. Steele, D. Lea, C. H. Flood, "Fast splittable pseudorandom
number generators". https://doi.org/10.1145/2714064.2660195

[3]: H. G. Schaathun, "Evaluation of splittable pseudo-random
generators". https://doi.org/10.1017/S095679681500012X

Performance (issue 51)
======================

The "improvement" column in the following table is a multiplier: the
improvement for 'random' for type 'Float' is 1038, so this operation is
1038 times faster with this patch.

| Name                    | Mean (1.1) | Mean (patch) | Improvement|
| ----------------------- | ---------- | ------------ | ---------- |
| pure/random/Float       |         30 |         0.03 |        1038|
| pure/random/Double      |         52 |         0.03 |        1672|
| pure/random/Integer     |         43 |         0.33 |         131|
| pure/uniform/Word8      |         14 |         0.03 |         422|
| pure/uniform/Word16     |         13 |         0.03 |         375|
| pure/uniform/Word32     |         21 |         0.03 |         594|
| pure/uniform/Word64     |         42 |         0.03 |        1283|
| pure/uniform/Word       |         44 |         0.03 |        1491|
| pure/uniform/Int8       |         15 |         0.03 |         511|
| pure/uniform/Int16      |         15 |         0.03 |         507|
| pure/uniform/Int32      |         22 |         0.03 |         749|
| pure/uniform/Int64      |         44 |         0.03 |        1405|
| pure/uniform/Int        |         43 |         0.03 |        1512|
| pure/uniform/Char       |         17 |         0.49 |          35|
| pure/uniform/Bool       |         18 |         0.03 |         618|
| pure/uniform/CChar      |         14 |         0.03 |         485|
| pure/uniform/CSChar     |         14 |         0.03 |         455|
| pure/uniform/CUChar     |         13 |         0.03 |         448|
| pure/uniform/CShort     |         14 |         0.03 |         473|
| pure/uniform/CUShort    |         13 |         0.03 |         457|
| pure/uniform/CInt       |         21 |         0.03 |         737|
| pure/uniform/CUInt      |         21 |         0.03 |         742|
| pure/uniform/CLong      |         43 |         0.03 |        1544|
| pure/uniform/CULong     |         42 |         0.03 |        1460|
| pure/uniform/CPtrdiff   |         43 |         0.03 |        1494|
| pure/uniform/CSize      |         43 |         0.03 |        1475|
| pure/uniform/CWchar     |         22 |         0.03 |         785|
| pure/uniform/CSigAtomic |         21 |         0.03 |         749|
| pure/uniform/CLLong     |         43 |         0.03 |        1554|
| pure/uniform/CULLong    |         42 |         0.03 |        1505|
| pure/uniform/CIntPtr    |         43 |         0.03 |        1476|
| pure/uniform/CUIntPtr   |         42 |         0.03 |        1463|
| pure/uniform/CIntMax    |         43 |         0.03 |        1535|
| pure/uniform/CUIntMax   |         42 |         0.03 |        1493|

API changes
===========

MonadRandom
-----------

This patch adds a class 'MonadRandom':

    -- | 'MonadRandom' is an interface to monadic pseudo-random number generators.
    class Monad m => MonadRandom g s m | g m -> s where
      {-# MINIMAL freezeGen,thawGen,(uniformWord32|uniformWord64) #-}

      type Frozen g = (f :: Type) | f -> g
      freezeGen :: g s -> m (Frozen g)
      thawGen :: Frozen g -> m (g s)

      uniformWord32 :: g s -> m Word32 -- default implementation in terms of uniformWord64
      uniformWord64 :: g s -> m Word64 -- default implementation in terms of uniformWord32
      -- plus methods for other word sizes and for byte strings
      -- all have default implementations so the MINIMAL pragma holds

Conceptually, in 'MonadRandom g s m', 'g s' is the type of the
generator, 's' is the state type, and 'm' the underlying monad. Via the
functional dependency 'g m -> s', the state type is determined by the
generator and monad.

'Frozen' is the type of the generator's state "at rest". It is defined
as an injective type family via 'f -> g', so there is no ambiguity as to
which 'g' any 'Frozen g' belongs to.

This definition is generic enough to accommodate, for example, the 'Gen'
type from the 'mwc-random' package, which itself abstracts over the
underlying primitive monad and state token. The documentation shows the
full 'MonadRandom Gen' instance.

Four 'MonadRandom' instances ("monadic adapters") are provided for pure
generators to enable their use in monadic code. The documentation
describes them in detail.

'Uniform' and 'UniformRange'
----------------------------

The 'Random' typeclass has conceptually been split into 'Uniform' and
'UniformRange'. The 'Random' typeclass is still included for backwards
compatibility. 'Uniform' is for types where it is possible to sample
from the type's entire domain; 'UniformRange' is for types where one can
sample from a specified range.

Breaking Changes
================

This patch introduces these breaking changes:

* requires 'base >= 4.10' (GHC-8.2)
* 'StdGen' is no longer an instance of 'Read'
* 'randomIO' and 'randomRIO' where extracted from the 'Random' class into
  separate functions

In addition, there may be import clashes with new functions, e.g. 'uniform' and
'uniformR'.

Deprecations
============

This patch introduces 'genWord64', 'genWord32' and similar methods to
the 'RandomGen' class. The significantly slower method 'next' and its
companion 'genRange' are now deprecated.

Co-authored-by: Alexey Kuleshevich <[email protected]>
Co-authored-by: idontgetoutmuch <[email protected]>
Co-authored-by: Leonhard Markert <[email protected]>
curiousleo added a commit to idontgetoutmuch/random that referenced this issue Jun 15, 2020
This patch is mostly backwards compatible. See "Breaking Changes" below
for the full list of backwards incompatible changes.

This patch fixes quality and performance issues, addresses additional
miscellaneous issues, and introduces a monadic API.

Issues addressed
================

Priority issues fixed in this patch:

- Title: "The seeds generated by split are not independent"
  Link:  haskell#25
  Fixed: changed algorithm to SplitMix, which provides a robust 'split'
  operation

- Title: "Very low throughput"
  Link:  haskell#51
  Fixed: see "Performance" below

Additional issues addressed in this patch:

- Title: "Add Random instances for tuples"
  Link:  haskell#26
  Addressed: added 'Uniform' instances for up to 6-tuples

- Title: "Add Random instance for Natural"
  Link:  haskell#44
  Addressed: added 'UniformRange' instance for 'Natural'

- Title: "incorrect distribution of randomR for floating-point numbers"
  Link:  haskell#53
  Addressed: see "Regarding floating-point numbers" below

- Title: "System/Random.hs:43:1: warning: [-Wtabs]"
  Link:  haskell#55
  Fixed: no more tabs

- Title: "Why does random for Float and Double produce exactly 24 or 53 bits?"
  Link:  haskell#58
  Fixed: see "Regarding floating-point numbers" below

- Title: "read :: StdGen fails for strings longer than 6"
  Link:  haskell#59
  Addressed: 'StdGen' is no longer an instance of 'Read'

Regarding floating-point numbers: with this patch, the relevant
instances for 'Float' and 'Double' sample more bits than before but do
not sample every possible representable value. The documentation now
clearly spells out what this means for users.

Quality (issue 25)
==================

The algorithm [1] in version 1.1 of this library fails empirical PRNG
tests when used to generate "split sequences" as proposed in [3].

SplitMix [2] passes the same tests. This patch changes 'StdGen' to use
the SplitMix implementation provided by the splitmix package.

Test batteries used: dieharder, TestU1, PractRand.

[1]: P. L'Ecuyer, "Efficient and portable combined random number
generators". https://doi.org/10.1145/62959.62969

[2]: G. L. Steele, D. Lea, C. H. Flood, "Fast splittable pseudorandom
number generators". https://doi.org/10.1145/2714064.2660195

[3]: H. G. Schaathun, "Evaluation of splittable pseudo-random
generators". https://doi.org/10.1017/S095679681500012X

Performance (issue 51)
======================

The "improvement" column in the following table is a multiplier: the
improvement for 'random' for type 'Float' is 1038, so this operation is
1038 times faster with this patch.

| Name                    | Mean (1.1) | Mean (patch) | Improvement|
| ----------------------- | ---------- | ------------ | ---------- |
| pure/random/Float       |         30 |         0.03 |        1038|
| pure/random/Double      |         52 |         0.03 |        1672|
| pure/random/Integer     |         43 |         0.33 |         131|
| pure/uniform/Word8      |         14 |         0.03 |         422|
| pure/uniform/Word16     |         13 |         0.03 |         375|
| pure/uniform/Word32     |         21 |         0.03 |         594|
| pure/uniform/Word64     |         42 |         0.03 |        1283|
| pure/uniform/Word       |         44 |         0.03 |        1491|
| pure/uniform/Int8       |         15 |         0.03 |         511|
| pure/uniform/Int16      |         15 |         0.03 |         507|
| pure/uniform/Int32      |         22 |         0.03 |         749|
| pure/uniform/Int64      |         44 |         0.03 |        1405|
| pure/uniform/Int        |         43 |         0.03 |        1512|
| pure/uniform/Char       |         17 |         0.49 |          35|
| pure/uniform/Bool       |         18 |         0.03 |         618|
| pure/uniform/CChar      |         14 |         0.03 |         485|
| pure/uniform/CSChar     |         14 |         0.03 |         455|
| pure/uniform/CUChar     |         13 |         0.03 |         448|
| pure/uniform/CShort     |         14 |         0.03 |         473|
| pure/uniform/CUShort    |         13 |         0.03 |         457|
| pure/uniform/CInt       |         21 |         0.03 |         737|
| pure/uniform/CUInt      |         21 |         0.03 |         742|
| pure/uniform/CLong      |         43 |         0.03 |        1544|
| pure/uniform/CULong     |         42 |         0.03 |        1460|
| pure/uniform/CPtrdiff   |         43 |         0.03 |        1494|
| pure/uniform/CSize      |         43 |         0.03 |        1475|
| pure/uniform/CWchar     |         22 |         0.03 |         785|
| pure/uniform/CSigAtomic |         21 |         0.03 |         749|
| pure/uniform/CLLong     |         43 |         0.03 |        1554|
| pure/uniform/CULLong    |         42 |         0.03 |        1505|
| pure/uniform/CIntPtr    |         43 |         0.03 |        1476|
| pure/uniform/CUIntPtr   |         42 |         0.03 |        1463|
| pure/uniform/CIntMax    |         43 |         0.03 |        1535|
| pure/uniform/CUIntMax   |         42 |         0.03 |        1493|

API changes
===========

MonadRandom
-----------

This patch adds a class 'MonadRandom':

    -- | 'MonadRandom' is an interface to monadic pseudo-random number generators.
    class Monad m => MonadRandom g s m | g m -> s where
      {-# MINIMAL freezeGen,thawGen,(uniformWord32|uniformWord64) #-}

      type Frozen g = (f :: Type) | f -> g
      freezeGen :: g s -> m (Frozen g)
      thawGen :: Frozen g -> m (g s)

      uniformWord32 :: g s -> m Word32 -- default implementation in terms of uniformWord64
      uniformWord64 :: g s -> m Word64 -- default implementation in terms of uniformWord32
      -- plus methods for other word sizes and for byte strings
      -- all have default implementations so the MINIMAL pragma holds

Conceptually, in 'MonadRandom g s m', 'g s' is the type of the
generator, 's' is the state type, and 'm' the underlying monad. Via the
functional dependency 'g m -> s', the state type is determined by the
generator and monad.

'Frozen' is the type of the generator's state "at rest". It is defined
as an injective type family via 'f -> g', so there is no ambiguity as to
which 'g' any 'Frozen g' belongs to.

This definition is generic enough to accommodate, for example, the 'Gen'
type from the 'mwc-random' package, which itself abstracts over the
underlying primitive monad and state token. The documentation shows the
full 'MonadRandom Gen' instance.

Four 'MonadRandom' instances ("monadic adapters") are provided for pure
generators to enable their use in monadic code. The documentation
describes them in detail.

'Uniform' and 'UniformRange'
----------------------------

The 'Random' typeclass has conceptually been split into 'Uniform' and
'UniformRange'. The 'Random' typeclass is still included for backwards
compatibility. 'Uniform' is for types where it is possible to sample
from the type's entire domain; 'UniformRange' is for types where one can
sample from a specified range.

Breaking Changes
================

This patch introduces these breaking changes:

* requires 'base >= 4.10' (GHC-8.2)
* 'StdGen' is no longer an instance of 'Read'
* 'randomIO' and 'randomRIO' where extracted from the 'Random' class into
  separate functions

In addition, there may be import clashes with new functions, e.g. 'uniform' and
'uniformR'.

Deprecations
============

This patch introduces 'genWord64', 'genWord32' and similar methods to
the 'RandomGen' class. The significantly slower method 'next' and its
companion 'genRange' are now deprecated.

Co-authored-by: Alexey Kuleshevich <[email protected]>
Co-authored-by: idontgetoutmuch <[email protected]>
Co-authored-by: Leonhard Markert <[email protected]>
curiousleo added a commit to idontgetoutmuch/random that referenced this issue Jun 22, 2020
This patch is mostly backwards compatible. See "Breaking Changes" below
for the full list of backwards incompatible changes.

This patch fixes quality and performance issues, addresses additional
miscellaneous issues, and introduces a monadic API.

Issues addressed
================

Priority issues fixed in this patch:

- Title: "The seeds generated by split are not independent"
  Link:  haskell#25
  Fixed: changed algorithm to SplitMix, which provides a robust 'split'
  operation

- Title: "Very low throughput"
  Link:  haskell#51
  Fixed: see "Performance" below

Additional issues addressed in this patch:

- Title: "Add Random instances for tuples"
  Link:  haskell#26
  Addressed: added 'Uniform' instances for up to 6-tuples

- Title: "Add Random instance for Natural"
  Link:  haskell#44
  Addressed: added 'UniformRange' instance for 'Natural'

- Title: "incorrect distribution of randomR for floating-point numbers"
  Link:  haskell#53
  Addressed: see "Regarding floating-point numbers" below

- Title: "System/Random.hs:43:1: warning: [-Wtabs]"
  Link:  haskell#55
  Fixed: no more tabs

- Title: "Why does random for Float and Double produce exactly 24 or 53 bits?"
  Link:  haskell#58
  Fixed: see "Regarding floating-point numbers" below

- Title: "read :: StdGen fails for strings longer than 6"
  Link:  haskell#59
  Addressed: 'StdGen' is no longer an instance of 'Read'

Regarding floating-point numbers: with this patch, the relevant
instances for 'Float' and 'Double' sample more bits than before but do
not sample every possible representable value. The documentation now
clearly spells out what this means for users.

Quality (issue 25)
==================

The algorithm [1] in version 1.1 of this library fails empirical PRNG
tests when used to generate "split sequences" as proposed in [3].

SplitMix [2] passes the same tests. This patch changes 'StdGen' to use
the SplitMix implementation provided by the splitmix package.

Test batteries used: dieharder, TestU1, PractRand.

[1]: P. L'Ecuyer, "Efficient and portable combined random number
generators". https://doi.org/10.1145/62959.62969

[2]: G. L. Steele, D. Lea, C. H. Flood, "Fast splittable pseudorandom
number generators". https://doi.org/10.1145/2714064.2660195

[3]: H. G. Schaathun, "Evaluation of splittable pseudo-random
generators". https://doi.org/10.1017/S095679681500012X

Performance (issue 51)
======================

The "improvement" column in the following table is a multiplier: the
improvement for 'random' for type 'Float' is 1038, so this operation is
1038 times faster with this patch.

| Name                    | Mean (1.1) | Mean (patch) | Improvement|
| ----------------------- | ---------- | ------------ | ---------- |
| pure/random/Float       |         30 |         0.03 |        1038|
| pure/random/Double      |         52 |         0.03 |        1672|
| pure/random/Integer     |         43 |         0.33 |         131|
| pure/uniform/Word8      |         14 |         0.03 |         422|
| pure/uniform/Word16     |         13 |         0.03 |         375|
| pure/uniform/Word32     |         21 |         0.03 |         594|
| pure/uniform/Word64     |         42 |         0.03 |        1283|
| pure/uniform/Word       |         44 |         0.03 |        1491|
| pure/uniform/Int8       |         15 |         0.03 |         511|
| pure/uniform/Int16      |         15 |         0.03 |         507|
| pure/uniform/Int32      |         22 |         0.03 |         749|
| pure/uniform/Int64      |         44 |         0.03 |        1405|
| pure/uniform/Int        |         43 |         0.03 |        1512|
| pure/uniform/Char       |         17 |         0.49 |          35|
| pure/uniform/Bool       |         18 |         0.03 |         618|
| pure/uniform/CChar      |         14 |         0.03 |         485|
| pure/uniform/CSChar     |         14 |         0.03 |         455|
| pure/uniform/CUChar     |         13 |         0.03 |         448|
| pure/uniform/CShort     |         14 |         0.03 |         473|
| pure/uniform/CUShort    |         13 |         0.03 |         457|
| pure/uniform/CInt       |         21 |         0.03 |         737|
| pure/uniform/CUInt      |         21 |         0.03 |         742|
| pure/uniform/CLong      |         43 |         0.03 |        1544|
| pure/uniform/CULong     |         42 |         0.03 |        1460|
| pure/uniform/CPtrdiff   |         43 |         0.03 |        1494|
| pure/uniform/CSize      |         43 |         0.03 |        1475|
| pure/uniform/CWchar     |         22 |         0.03 |         785|
| pure/uniform/CSigAtomic |         21 |         0.03 |         749|
| pure/uniform/CLLong     |         43 |         0.03 |        1554|
| pure/uniform/CULLong    |         42 |         0.03 |        1505|
| pure/uniform/CIntPtr    |         43 |         0.03 |        1476|
| pure/uniform/CUIntPtr   |         42 |         0.03 |        1463|
| pure/uniform/CIntMax    |         43 |         0.03 |        1535|
| pure/uniform/CUIntMax   |         42 |         0.03 |        1493|

API changes
===========

StatefulGen
-----------

This patch adds a class 'StatefulGen':

    -- | 'StatefulGen' is an interface to monadic pseudo-random number generators.
    class Monad m => StatefulGen g m where
      uniformWord32 :: g -> m Word32 -- default implementation in terms of uniformWord64
      uniformWord64 :: g -> m Word64 -- default implementation in terms of uniformWord32
      -- plus methods for other word sizes and for byte strings
      -- all have default implementations so the MINIMAL pragma holds

In 'StatefulGen g m', 'g' is the type of the generator and 'm' the underlying
monad.

Four 'StatefulGen' instances ("monadic adapters") are provided for pure
generators to enable their use in monadic code. The documentation
describes them in detail.

FrozenGen
---------

This patch also introduces a class 'FrozenGen':

    -- | 'FrozenGen' is designed for stateful pseudo-random number generators
    -- that can be saved as and restored from an immutable data type.
    class StatefulGen (MutableGen f m) m => FrozenGen f m where
      type MutableGen f m = (g :: Type) | g -> f
      freezeGen :: MutableGen f m -> m f
      thawGen :: f -> m (MutableGen f m)

'f' is the type of the generator's state "at rest" and 'm' the underlying
monad. 'MutableGen' is defined as an injective type family via 'g -> f' so for
any generator 'g', the type 'f' of its at-rest state is well-defined.

Both 'StatefulGen' and 'FrozenGen' are generic enough to accommodate, for
example, the 'Gen' type from the 'mwc-random' package, which itself abstracts
over the underlying primitive monad and state token. The documentation shows
the full instances.

'Uniform' and 'UniformRange'
----------------------------

The 'Random' typeclass has conceptually been split into 'Uniform' and
'UniformRange'. The 'Random' typeclass is still included for backwards
compatibility. 'Uniform' is for types where it is possible to sample
from the type's entire domain; 'UniformRange' is for types where one can
sample from a specified range.

Breaking Changes
================

This patch introduces these breaking changes:

* requires 'base >= 4.10' (GHC-8.2)
* 'StdGen' is no longer an instance of 'Read'
* 'randomIO' and 'randomRIO' where extracted from the 'Random' class into
  separate functions

In addition, there may be import clashes with new functions, e.g. 'uniform' and
'uniformR'.

Deprecations
============

This patch introduces 'genWord64', 'genWord32' and similar methods to
the 'RandomGen' class. The significantly slower method 'next' and its
companion 'genRange' are now deprecated.

Co-authored-by: Alexey Kuleshevich <[email protected]>
Co-authored-by: idontgetoutmuch <[email protected]>
Co-authored-by: Leonhard Markert <[email protected]>
curiousleo added a commit to idontgetoutmuch/random that referenced this issue Jun 22, 2020
This patch is mostly backwards compatible. See "Breaking Changes" below
for the full list of backwards incompatible changes.

This patch fixes quality and performance issues, addresses additional
miscellaneous issues, and introduces a monadic API.

Issues addressed
================

Priority issues fixed in this patch:

- Title: "The seeds generated by split are not independent"
  Link:  haskell#25
  Fixed: changed algorithm to SplitMix, which provides a robust 'split'
  operation

- Title: "Very low throughput"
  Link:  haskell#51
  Fixed: see "Performance" below

Additional issues addressed in this patch:

- Title: "Add Random instances for tuples"
  Link:  haskell#26
  Addressed: added 'Uniform' instances for up to 6-tuples

- Title: "Add Random instance for Natural"
  Link:  haskell#44
  Addressed: added 'UniformRange' instance for 'Natural'

- Title: "incorrect distribution of randomR for floating-point numbers"
  Link:  haskell#53
  Addressed: see "Regarding floating-point numbers" below

- Title: "System/Random.hs:43:1: warning: [-Wtabs]"
  Link:  haskell#55
  Fixed: no more tabs

- Title: "Why does random for Float and Double produce exactly 24 or 53 bits?"
  Link:  haskell#58
  Fixed: see "Regarding floating-point numbers" below

- Title: "read :: StdGen fails for strings longer than 6"
  Link:  haskell#59
  Addressed: 'StdGen' is no longer an instance of 'Read'

Regarding floating-point numbers: with this patch, the relevant
instances for 'Float' and 'Double' sample more bits than before but do
not sample every possible representable value. The documentation now
clearly spells out what this means for users.

Quality (issue 25)
==================

The algorithm [1] in version 1.1 of this library fails empirical PRNG
tests when used to generate "split sequences" as proposed in [3].

SplitMix [2] passes the same tests. This patch changes 'StdGen' to use
the SplitMix implementation provided by the splitmix package.

Test batteries used: dieharder, TestU1, PractRand.

[1]: P. L'Ecuyer, "Efficient and portable combined random number
generators". https://doi.org/10.1145/62959.62969

[2]: G. L. Steele, D. Lea, C. H. Flood, "Fast splittable pseudorandom
number generators". https://doi.org/10.1145/2714064.2660195

[3]: H. G. Schaathun, "Evaluation of splittable pseudo-random
generators". https://doi.org/10.1017/S095679681500012X

Performance (issue 51)
======================

The "improvement" column in the following table is a multiplier: the
improvement for 'random' for type 'Float' is 1038, so this operation is
1038 times faster with this patch.

| Name                    | Mean (1.1) | Mean (patch) | Improvement|
| ----------------------- | ---------- | ------------ | ---------- |
| pure/random/Float       |         30 |         0.03 |        1038|
| pure/random/Double      |         52 |         0.03 |        1672|
| pure/random/Integer     |         43 |         0.33 |         131|
| pure/uniform/Word8      |         14 |         0.03 |         422|
| pure/uniform/Word16     |         13 |         0.03 |         375|
| pure/uniform/Word32     |         21 |         0.03 |         594|
| pure/uniform/Word64     |         42 |         0.03 |        1283|
| pure/uniform/Word       |         44 |         0.03 |        1491|
| pure/uniform/Int8       |         15 |         0.03 |         511|
| pure/uniform/Int16      |         15 |         0.03 |         507|
| pure/uniform/Int32      |         22 |         0.03 |         749|
| pure/uniform/Int64      |         44 |         0.03 |        1405|
| pure/uniform/Int        |         43 |         0.03 |        1512|
| pure/uniform/Char       |         17 |         0.49 |          35|
| pure/uniform/Bool       |         18 |         0.03 |         618|
| pure/uniform/CChar      |         14 |         0.03 |         485|
| pure/uniform/CSChar     |         14 |         0.03 |         455|
| pure/uniform/CUChar     |         13 |         0.03 |         448|
| pure/uniform/CShort     |         14 |         0.03 |         473|
| pure/uniform/CUShort    |         13 |         0.03 |         457|
| pure/uniform/CInt       |         21 |         0.03 |         737|
| pure/uniform/CUInt      |         21 |         0.03 |         742|
| pure/uniform/CLong      |         43 |         0.03 |        1544|
| pure/uniform/CULong     |         42 |         0.03 |        1460|
| pure/uniform/CPtrdiff   |         43 |         0.03 |        1494|
| pure/uniform/CSize      |         43 |         0.03 |        1475|
| pure/uniform/CWchar     |         22 |         0.03 |         785|
| pure/uniform/CSigAtomic |         21 |         0.03 |         749|
| pure/uniform/CLLong     |         43 |         0.03 |        1554|
| pure/uniform/CULLong    |         42 |         0.03 |        1505|
| pure/uniform/CIntPtr    |         43 |         0.03 |        1476|
| pure/uniform/CUIntPtr   |         42 |         0.03 |        1463|
| pure/uniform/CIntMax    |         43 |         0.03 |        1535|
| pure/uniform/CUIntMax   |         42 |         0.03 |        1493|

API changes
===========

StatefulGen
-----------

This patch adds a class 'StatefulGen':

    -- | 'StatefulGen' is an interface to monadic pseudo-random number generators.
    class Monad m => StatefulGen g m where
      uniformWord32 :: g -> m Word32 -- default implementation in terms of uniformWord64
      uniformWord64 :: g -> m Word64 -- default implementation in terms of uniformWord32
      -- plus methods for other word sizes and for byte strings
      -- all have default implementations so the MINIMAL pragma holds

In 'StatefulGen g m', 'g' is the type of the generator and 'm' the underlying
monad.

Four 'StatefulGen' instances ("monadic adapters") are provided for pure
generators to enable their use in monadic code. The documentation
describes them in detail.

FrozenGen
---------

This patch also introduces a class 'FrozenGen':

    -- | 'FrozenGen' is designed for stateful pseudo-random number generators
    -- that can be saved as and restored from an immutable data type.
    class StatefulGen (MutableGen f m) m => FrozenGen f m where
      type MutableGen f m = (g :: Type) | g -> f
      freezeGen :: MutableGen f m -> m f
      thawGen :: f -> m (MutableGen f m)

'f' is the type of the generator's state "at rest" and 'm' the underlying
monad. 'MutableGen' is defined as an injective type family via 'g -> f' so for
any generator 'g', the type 'f' of its at-rest state is well-defined.

Both 'StatefulGen' and 'FrozenGen' are generic enough to accommodate, for
example, the 'Gen' type from the 'mwc-random' package, which itself abstracts
over the underlying primitive monad and state token. The documentation shows
the full instances.

'Uniform' and 'UniformRange'
----------------------------

The 'Random' typeclass has conceptually been split into 'Uniform' and
'UniformRange'. The 'Random' typeclass is still included for backwards
compatibility. 'Uniform' is for types where it is possible to sample
from the type's entire domain; 'UniformRange' is for types where one can
sample from a specified range.

Breaking Changes
================

This patch introduces these breaking changes:

* requires 'base >= 4.10' (GHC-8.2)
* 'StdGen' is no longer an instance of 'Read'
* 'randomIO' and 'randomRIO' where extracted from the 'Random' class into
  separate functions

In addition, there may be import clashes with new functions, e.g. 'uniform' and
'uniformR'.

Deprecations
============

This patch introduces 'genWord64', 'genWord32' and similar methods to
the 'RandomGen' class. The significantly slower method 'next' and its
companion 'genRange' are now deprecated.

Co-authored-by: Alexey Kuleshevich <[email protected]>
Co-authored-by: idontgetoutmuch <[email protected]>
Co-authored-by: Leonhard Markert <[email protected]>
curiousleo added a commit to idontgetoutmuch/random that referenced this issue Jun 23, 2020
This patch is mostly backwards compatible. See "Breaking Changes" below
for the full list of backwards incompatible changes.

This patch fixes quality and performance issues, addresses additional
miscellaneous issues, and introduces a monadic API.

Issues addressed
================

Priority issues fixed in this patch:

- Title: "The seeds generated by split are not independent"
  Link:  haskell#25
  Fixed: changed algorithm to SplitMix, which provides a robust 'split'
  operation

- Title: "Very low throughput"
  Link:  haskell#51
  Fixed: see "Performance" below

Additional issues addressed in this patch:

- Title: "Add Random instances for tuples"
  Link:  haskell#26
  Addressed: added 'Uniform' instances for up to 6-tuples

- Title: "Add Random instance for Natural"
  Link:  haskell#44
  Addressed: added 'UniformRange' instance for 'Natural'

- Title: "incorrect distribution of randomR for floating-point numbers"
  Link:  haskell#53
  Addressed: see "Regarding floating-point numbers" below

- Title: "System/Random.hs:43:1: warning: [-Wtabs]"
  Link:  haskell#55
  Fixed: no more tabs

- Title: "Why does random for Float and Double produce exactly 24 or 53 bits?"
  Link:  haskell#58
  Fixed: see "Regarding floating-point numbers" below

- Title: "read :: StdGen fails for strings longer than 6"
  Link:  haskell#59
  Addressed: 'StdGen' is no longer an instance of 'Read'

Regarding floating-point numbers: with this patch, the relevant
instances for 'Float' and 'Double' sample more bits than before but do
not sample every possible representable value. The documentation now
clearly spells out what this means for users.

Quality (issue 25)
==================

The algorithm [1] in version 1.1 of this library fails empirical PRNG
tests when used to generate "split sequences" as proposed in [3].

SplitMix [2] passes the same tests. This patch changes 'StdGen' to use
the SplitMix implementation provided by the splitmix package.

Test batteries used: dieharder, TestU1, PractRand.

[1]: P. L'Ecuyer, "Efficient and portable combined random number
generators". https://doi.org/10.1145/62959.62969

[2]: G. L. Steele, D. Lea, C. H. Flood, "Fast splittable pseudorandom
number generators". https://doi.org/10.1145/2714064.2660195

[3]: H. G. Schaathun, "Evaluation of splittable pseudo-random
generators". https://doi.org/10.1017/S095679681500012X

Performance (issue 51)
======================

The "improvement" column in the following table is a multiplier: the
improvement for 'random' for type 'Float' is 1038, so this operation is
1038 times faster with this patch.

| Name                    | Mean (1.1) | Mean (patch) | Improvement|
| ----------------------- | ---------- | ------------ | ---------- |
| pure/random/Float       |         30 |         0.03 |        1038|
| pure/random/Double      |         52 |         0.03 |        1672|
| pure/random/Integer     |         43 |         0.33 |         131|
| pure/uniform/Word       |         44 |         0.03 |        1491|
| pure/uniform/Int        |         43 |         0.03 |        1512|
| pure/uniform/Char       |         17 |         0.49 |          35|
| pure/uniform/Bool       |         18 |         0.03 |         618|

API changes
===========

StatefulGen
-----------

This patch adds a class 'StatefulGen':

    -- | 'StatefulGen' is an interface to monadic pseudo-random number generators.
    class Monad m => StatefulGen g m where
      uniformWord32 :: g -> m Word32 -- default implementation in terms of uniformWord64
      uniformWord64 :: g -> m Word64 -- default implementation in terms of uniformWord32
      -- plus methods for other word sizes and for byte strings
      -- all have default implementations so the MINIMAL pragma holds

In 'StatefulGen g m', 'g' is the type of the generator and 'm' the underlying
monad.

Four 'StatefulGen' instances ("monadic adapters") are provided for pure
generators to enable their use in monadic code. The documentation
describes them in detail.

FrozenGen
---------

This patch also introduces a class 'FrozenGen':

    -- | 'FrozenGen' is designed for stateful pseudo-random number generators
    -- that can be saved as and restored from an immutable data type.
    class StatefulGen (MutableGen f m) m => FrozenGen f m where
      type MutableGen f m = (g :: Type) | g -> f
      freezeGen :: MutableGen f m -> m f
      thawGen :: f -> m (MutableGen f m)

'f' is the type of the generator's state "at rest" and 'm' the underlying
monad. 'MutableGen' is defined as an injective type family via 'g -> f' so for
any generator 'g', the type 'f' of its at-rest state is well-defined.

Both 'StatefulGen' and 'FrozenGen' are generic enough to accommodate, for
example, the 'Gen' type from the 'mwc-random' package, which itself abstracts
over the underlying primitive monad and state token. The documentation shows
the full instances.

'Uniform' and 'UniformRange'
----------------------------

The 'Random' typeclass has conceptually been split into 'Uniform' and
'UniformRange'. The 'Random' typeclass is still included for backwards
compatibility. 'Uniform' is for types where it is possible to sample
from the type's entire domain; 'UniformRange' is for types where one can
sample from a specified range.

Breaking Changes
================

This patch introduces these breaking changes:

* requires 'base >= 4.8' (GHC-7.10)
* 'StdGen' is no longer an instance of 'Read'
* 'randomIO' and 'randomRIO' where extracted from the 'Random' class into
  separate functions

In addition, there may be import clashes with new functions, e.g. 'uniform' and
'uniformR'.

Deprecations
============

This patch introduces 'genWord64', 'genWord32' and similar methods to
the 'RandomGen' class. The significantly slower method 'next' and its
companion 'genRange' are now deprecated.

Co-authored-by: Alexey Kuleshevich <[email protected]>
Co-authored-by: idontgetoutmuch <[email protected]>
Co-authored-by: Leonhard Markert <[email protected]>
curiousleo added a commit to idontgetoutmuch/random that referenced this issue Jun 23, 2020
This patch is mostly backwards compatible. See "Breaking Changes" below
for the full list of backwards incompatible changes.

This patch fixes quality and performance issues, addresses additional
miscellaneous issues, and introduces a monadic API.

Issues addressed
================

Priority issues fixed in this patch:

- Title: "The seeds generated by split are not independent"
  Link:  haskell#25
  Fixed: changed algorithm to SplitMix, which provides a robust 'split'
  operation

- Title: "Very low throughput"
  Link:  haskell#51
  Fixed: see "Performance" below

Additional issues addressed in this patch:

- Title: "Add Random instances for tuples"
  Link:  haskell#26
  Addressed: added 'Uniform' instances for up to 6-tuples

- Title: "Add Random instance for Natural"
  Link:  haskell#44
  Addressed: added 'UniformRange' instance for 'Natural'

- Title: "incorrect distribution of randomR for floating-point numbers"
  Link:  haskell#53
  Addressed: see "Regarding floating-point numbers" below

- Title: "System/Random.hs:43:1: warning: [-Wtabs]"
  Link:  haskell#55
  Fixed: no more tabs

- Title: "Why does random for Float and Double produce exactly 24 or 53 bits?"
  Link:  haskell#58
  Fixed: see "Regarding floating-point numbers" below

- Title: "read :: StdGen fails for strings longer than 6"
  Link:  haskell#59
  Addressed: 'StdGen' is no longer an instance of 'Read'

Regarding floating-point numbers: with this patch, the relevant
instances for 'Float' and 'Double' sample more bits than before but do
not sample every possible representable value. The documentation now
clearly spells out what this means for users.

Quality (issue 25)
==================

The algorithm [1] in version 1.1 of this library fails empirical PRNG
tests when used to generate "split sequences" as proposed in [3].

SplitMix [2] passes the same tests. This patch changes 'StdGen' to use
the SplitMix implementation provided by the splitmix package.

Test batteries used: dieharder, TestU1, PractRand.

[1]: P. L'Ecuyer, "Efficient and portable combined random number
generators". https://doi.org/10.1145/62959.62969

[2]: G. L. Steele, D. Lea, C. H. Flood, "Fast splittable pseudorandom
number generators". https://doi.org/10.1145/2714064.2660195

[3]: H. G. Schaathun, "Evaluation of splittable pseudo-random
generators". https://doi.org/10.1017/S095679681500012X

Performance (issue 51)
======================

The "improvement" column in the following table is a multiplier: the
improvement for 'random' for type 'Float' is 1038, so this operation is
1038 times faster with this patch.

| Name                    | Mean (1.1) | Mean (patch) | Improvement|
| ----------------------- | ---------- | ------------ | ---------- |
| pure/random/Float       |         30 |         0.03 |        1038|
| pure/random/Double      |         52 |         0.03 |        1672|
| pure/random/Integer     |         43 |         0.33 |         131|
| pure/uniform/Word       |         44 |         0.03 |        1491|
| pure/uniform/Int        |         43 |         0.03 |        1512|
| pure/uniform/Char       |         17 |         0.49 |          35|
| pure/uniform/Bool       |         18 |         0.03 |         618|

API changes
===========

StatefulGen
-----------

This patch adds a class 'StatefulGen':

    -- | 'StatefulGen' is an interface to monadic pseudo-random number generators.
    class Monad m => StatefulGen g m where
      uniformWord32 :: g -> m Word32 -- default implementation in terms of uniformWord64
      uniformWord64 :: g -> m Word64 -- default implementation in terms of uniformWord32
      -- plus methods for other word sizes and for byte strings
      -- all have default implementations so the MINIMAL pragma holds

In 'StatefulGen g m', 'g' is the type of the generator and 'm' the underlying
monad.

Four 'StatefulGen' instances ("monadic adapters") are provided for pure
generators to enable their use in monadic code. The documentation
describes them in detail.

FrozenGen
---------

This patch also introduces a class 'FrozenGen':

    -- | 'FrozenGen' is designed for stateful pseudo-random number generators
    -- that can be saved as and restored from an immutable data type.
    class StatefulGen (MutableGen f m) m => FrozenGen f m where
      type MutableGen f m = (g :: Type) | g -> f
      freezeGen :: MutableGen f m -> m f
      thawGen :: f -> m (MutableGen f m)

'f' is the type of the generator's state "at rest" and 'm' the underlying
monad. 'MutableGen' is defined as an injective type family via 'g -> f' so for
any generator 'g', the type 'f' of its at-rest state is well-defined.

Both 'StatefulGen' and 'FrozenGen' are generic enough to accommodate, for
example, the 'Gen' type from the 'mwc-random' package, which itself abstracts
over the underlying primitive monad and state token. The documentation shows
the full instances.

'Uniform' and 'UniformRange'
----------------------------

The 'Random' typeclass has conceptually been split into 'Uniform' and
'UniformRange'. The 'Random' typeclass is still included for backwards
compatibility. 'Uniform' is for types where it is possible to sample
from the type's entire domain; 'UniformRange' is for types where one can
sample from a specified range.

Breaking Changes
================

This patch introduces these breaking changes:

* requires 'base >= 4.8' (GHC-7.10)
* 'StdGen' is no longer an instance of 'Read'
* 'randomIO' and 'randomRIO' where extracted from the 'Random' class into
  separate functions

In addition, there may be import clashes with new functions, e.g. 'uniform' and
'uniformR'.

Deprecations
============

This patch introduces 'genWord64', 'genWord32' and similar methods to
the 'RandomGen' class. The significantly slower method 'next' and its
companion 'genRange' are now deprecated.

Co-authored-by: Alexey Kuleshevich <[email protected]>
Co-authored-by: idontgetoutmuch <[email protected]>
Co-authored-by: Leonhard Markert <[email protected]>
@lehins
Copy link
Contributor

lehins commented Jun 23, 2020

There are now up 7-tuple instances for Uniform type class, so by using uniform it is already possible to get random tuples. I don't see a reason why we can't add such instances to Random as well. There was too many changes already in random-1.2, so we'll add those instances as well as a few other types in the next release.

@Bodigrim
Copy link
Contributor

Random for tuples requires implementing randomR to generate a random value in a range. Should we define UniformRange instances for tuples as well then?

@Shimuuar
Copy link
Contributor

It's impossible in our current implementation. In order to define such instance we need to be able to count number of elements in range from (a1,b1) to (a2,b2) which in turn require knowing number of values inhabiting b. It wouldn't be efficient either number of inhabitants growths fast. Consider for example (Int64,Int64,Int64). On top of that instance doesn't make much sense

@Bodigrim
Copy link
Contributor

@ndmitchell proposed above that "between (a,b) and (c,d) should probably be equivalent to the first member in the range a-c and the second in b-d". Under this interpretation we can define UniformRange, but I'm not quite convinced that this is a right and unambiguous thing to do. (That's basically what I meant to ask in the comment above)

@lehins
Copy link
Contributor

lehins commented Jun 24, 2020

I think we can define such instance if we treat a and b completely independent of each other. For example this could work:

instance (UniformRange a, UniformRange b) => UniformRange (a, b) where
  uniformRM ((al, bl), (ah, bh)) g = do
    a <- uniformRM (al, ah) g
    b <- uniformRM (bl, bh) g
    pure (a, b)

If we think of the tuples as some related value such complex numbers or something then it definitely doesn't make sense, but above instance (and similar one for Random) I believe could be pretty useful. @Shimuuar What's your take on this?

@Shimuuar
Copy link
Contributor

That would be easy to implement. And in some sense it is uniform. But it's wrong is we use interpretation: "sample uniformly every value between a and b". I think it's better to avoid defining weird instances from get-go

@lehins
Copy link
Contributor

lehins commented Jun 24, 2020

In that case, I think it would be ok to add such instance for Random, but not for UniformRange, since former already does not promise uniformity as with types like Float and Integer.

@Bodigrim
Copy link
Contributor

Defining more instances of Random than of UniformRange will motivate users to use the former and block its eventual deprecation. If we think that this is a handy instance, let's add it to UniformRange as well with a proper comment/clarification.

@lehins
Copy link
Contributor

lehins commented Jun 24, 2020

I certainly disagree with Random being deprecated, even over time! After over two decades of people using it, this would be a wrong move. Moreover, many programming languages have a concept like "Random", that produces some standard distribution that does not have a mathematical backing but is useful for programmers. Tuples here is a perfect example.

Users can choose themselves which instance they want to use. One says Uniform and it promises the uniform distribution, another one is Random, which makes it open to interpretation. Many of the types do coincide and that is fine, IMHO.

@Bodigrim
Copy link
Contributor

The problem is that currently Random and randomR are not open to intepretation: they do claim uniformity.

random/src/System/Random.hs

Lines 189 to 190 in 11464aa

-- | The class of types for which uniformly distributed values can be
-- generated.

random/src/System/Random.hs

Lines 197 to 199 in 11464aa

-- | Takes a range /(lo,hi)/ and a pseudo-random number generator
-- /g/, and returns a pseudo-random value uniformly distributed over the
-- closed interval /[lo,hi]/, together with a new generator. It is unspecified

I do not particularly mind lifting this guarantee for Random, but I think that in future with a wider adoption of UniformRange people would ask for UniformRange for tuples as well. I would rather define either both, or none.

@ndmitchell
Copy link
Author

FWIW, I have no particular desire for a uniform range in 99%+ of cases. I've wanted random tuples a handful of times. I'm perfectly happy with Random.

@lehins
Copy link
Contributor

lehins commented Jun 24, 2020

@Bodigrim the whole point of the Uniform and UniformRange classes coming into existence was for them to be different from Random type class. So when "people do ask", I'll be happy to tell them such instance cannot exist, but for now let's not worry about what "people might ask".

I am on the same page with @ndmitchell and @Shimuuar seems to support this idea as well.

So, let's remove the "uniform" promise from Random class, because it is already a lie and add some tuple instances for Random, but not UniformRange.

@Bodigrim
Copy link
Contributor

If we communicate the difference between Random and Uniform clearly and unambiguously, then I'm on board as well.

@curiousleo
Copy link
Contributor

@lehins wrote:

add some tuple instances for Random, but not UniformRange.

I'm on board with this conclusion.

So, let's remove the "uniform" promise from Random class, because it is already a lie

I don't understand this premise. Which Random instances do not produce uniform distributions?

@lehins
Copy link
Contributor

lehins commented Jun 25, 2020

Which Random instances do not produce uniform distributions?

Integer, Float and Double. Calling random function for these types will produce values distributed uniformly in a subrange, instead of full range, because as you know full range is infinite

@lehins
Copy link
Contributor

lehins commented Jun 29, 2020

There is implementation for this ticket in #72 if anyone feels like giving it a review.

@lehins
Copy link
Contributor

lehins commented Sep 19, 2021

Finally this is implemented and is merged into master. These instances will be soon released with random-1.2.1

@lehins lehins closed this as completed Sep 19, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

8 participants