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

more methods working with a non-global RNG #8380

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions base/bitarray.jl
Original file line number Diff line number Diff line change
Expand Up @@ -331,10 +331,10 @@ bitpack{T,N}(A::AbstractArray{T,N}) = convert(BitArray{N}, A)

## Random ##

function bitarray_rand_fill!(B::BitArray)
function bitarray_rand_fill!(r, B::BitArray) # r is an AbstractRNG
length(B) == 0 && return B
Bc = B.chunks
rand!(Bc)
rand!(r, Bc)
Bc[end] &= @_msk_end length(B)
return B
end
Expand Down
27 changes: 24 additions & 3 deletions base/int.jl
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,13 @@ typealias SmallSigned Union(Int8,Int16,Int32)
typealias SmallUnsigned Union(Uint8,Uint16,Uint32)
end

typealias Signed64 Union(Int8,Int16,Int32,Int64)
typealias Unsigned64 Union(Uint8,Uint16,Uint32,Uint64)
typealias Integer64 Union(Signed64,Unsigned64)
typealias Signed128 Union(Signed64, Int128)
typealias Unsigned128 Union(Unsigned64, Uint128)
typealias Integer128 Union(Signed128,Unsigned128)

## integer arithmetic ##

-(x::SmallSigned) = -int(x)
Expand Down Expand Up @@ -93,9 +100,6 @@ cld(x::Unsigned, y::Signed) = div(x,y)+(!signbit(y)&(rem(x,y)!=0))

# Don't promote integers for div/rem/mod since there no danger of overflow,
# while there is a substantial performance penalty to 64-bit promotion.
typealias Signed64 Union(Int8,Int16,Int32,Int64)
typealias Unsigned64 Union(Uint8,Uint16,Uint32,Uint64)
typealias Integer64 Union(Signed64,Unsigned64)

div{T<:Signed64} (x::T, y::T) = box(T,sdiv_int(unbox(T,x),unbox(T,y)))
div{T<:Unsigned64}(x::T, y::T) = box(T,udiv_int(unbox(T,x),unbox(T,y)))
Expand Down Expand Up @@ -371,6 +375,23 @@ signed(x) = convert(Signed,x)
unsigned(x) = convert(Unsigned,x)
integer(x) = convert(Integer,x)

as_signed{T<:Signed128}(::Type{T}) = T
as_signed(::Type{Uint8} ) = Int8
as_signed(::Type{Uint16} ) = Int16
as_signed(::Type{Uint32} ) = Int32
as_signed(::Type{Uint64} ) = Int64
as_signed(::Type{Uint128}) = Int128

as_unsigned{T<:Unsigned128}(::Type{T}) = T
as_unsigned(::Type{Int8} ) = Uint8
as_unsigned(::Type{Int16} ) = Uint16
as_unsigned(::Type{Int32} ) = Uint32
as_unsigned(::Type{Int64} ) = Uint64
as_unsigned(::Type{Int128}) = Uint128

as_signed{T<:Integer128}(x::T) = convert(as_signed(T), x)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Doesn't convert(Signed, x) already do this?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then you could just do Signed(x) and Unsigned(x)...

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's weird, I remember having answered ("no") to Jeff's above comment but can't see it: can an inline comment be deleted when rebasing?

Yes this PR needs to be updated and rebased, unless it is deemed worthless.

as_unsigned{T<:Integer128}(x::T) = convert(as_unsigned(T), x)

round(x::Integer) = x
trunc(x::Integer) = x
floor(x::Integer) = x
Expand Down
155 changes: 85 additions & 70 deletions base/random.jl
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module Random

using Base.dSFMT
using Base: dSFMT, Integer128, Signed128, as_unsigned

export srand,
rand, rand!,
Expand All @@ -13,57 +13,65 @@ abstract AbstractRNG

type MersenneTwister <: AbstractRNG
state::DSFMT_state
seed::Union(Uint32,Vector{Uint32})
seed::Vector{Uint32}

function MersenneTwister(seed::Vector{Uint32})
function MersenneTwister(seed::Union(Integer,Vector{Uint32}))
state = DSFMT_state()
dsfmt_init_by_array(state, seed)
return new(state, seed)
return srand(new(state), seed)
end

MersenneTwister(seed=0) = MersenneTwister(make_seed(seed))
MersenneTwister() = MersenneTwister(0)
end

function srand(r::MersenneTwister, seed)
r.seed = seed
dsfmt_init_gen_rand(r.state, seed)
return r
immutable GlobalRNG <: AbstractRNG
end

typealias MT Union(GlobalRNG, MersenneTwister)

# random numbers of the following types are implemented:
MTRealTypes = Union(Bool, Integer128, Float16, Float32, Float64)
MTTypes = Union(MTRealTypes, [Complex{T} for T in MTRealTypes.types]...)

## rand: a non-specified RNG defaults to GlobalRNG()

rand() = rand(GlobalRNG())
rand{T<:MTTypes}(::Type{T}) = rand(GlobalRNG(), T)
rand(dims::Dims) = rand(GlobalRNG(), dims)
rand(dims::Int...) = rand(dims)
rand{T<:MTTypes}(::Type{T}, dims::Dims) = rand(GlobalRNG(), T, dims)
rand{T<:MTTypes}(::Type{T}, d1::Int, dims::Int...) = rand(T, tuple(d1, dims...))
rand!{T<:MTTypes}(A::AbstractArray{T}) = rand!(GlobalRNG(), A)

## initialization

function srand()
__init__() = srand()

## make_seed()

function make_seed()

@unix_only begin
try
srand("/dev/urandom")
return make_seed("/dev/urandom", 4)
catch
println(STDERR, "Entropy pool not available to seed RNG; using ad-hoc entropy sources.")
seed = reinterpret(Uint64, time())
seed = hash(seed, uint64(getpid()))
try
seed = hash(seed, parseint(Uint64, readall(`ifconfig` |> `sha1sum`)[1:40], 16))
end
srand(seed)
return make_seed(seed)
end
end

@windows_only begin
a = zeros(Uint32, 2)
win32_SystemFunction036!(a)
srand(a)
return make_seed(a)
end
end

__init__() = srand()

## srand()

function srand(seed::Vector{Uint32})
global RANDOM_SEED = seed
dsfmt_gv_init_by_array(seed)
end
srand(n::Integer) = srand(make_seed(n))
make_seed(seed::Vector{Uint32}) = seed

function make_seed(n::Integer)
n < 0 && throw(DomainError())
Expand All @@ -77,75 +85,80 @@ function make_seed(n::Integer)
end
end

function srand(filename::String, n::Integer)
function make_seed(filename::String, n::Integer)
open(filename) do io
a = Array(Uint32, int(n))
read!(io, a)
srand(a)
a
end
end
srand(filename::String) = srand(filename, 4)

## random floating point values
## srand()

rand(::Type{Float64}) = dsfmt_gv_genrand_close_open()
rand() = dsfmt_gv_genrand_close_open()
srand() = srand(make_seed())

rand(::Type{Float32}) = float32(rand())
rand(::Type{Float16}) = float16(rand())
srand(r::MersenneTwister) = srand(r, make_seed())

rand{T<:Real}(::Type{Complex{T}}) = complex(rand(T),rand(T))
function srand(seed::Union(Integer, Vector{Uint32}))
global RANDOM_SEED = make_seed(seed)
dsfmt_gv_init_by_array(RANDOM_SEED)
end

function srand(r::MersenneTwister, seed::Union(Integer, Vector{Uint32}))
r.seed = make_seed(seed)
dsfmt_init_by_array(r.state, r.seed)
return r
end

srand(filename::String, n::Integer=4) = srand(make_seed(filename, n))

srand(r::MersenneTwister, filename::String, n::Integer=4) = srand(r, make_seed(filename, n))

## random floating point values

rand(r::AbstractRNG) = rand(r, Float64)

# MersenneTwister
rand(r::GlobalRNG, ::Type{Float64}) = dsfmt_gv_genrand_close_open()
rand(r::MersenneTwister, ::Type{Float64}) = dsfmt_genrand_close_open(r.state)

rand(r::MersenneTwister) = dsfmt_genrand_close_open(r.state)
rand{T<:Union(Float16, Float32)}(r::MT, ::Type{T}) = convert(T, rand(r))

## random integers
## random integers (MersenneTwister)

dsfmt_randui32() = dsfmt_gv_genrand_uint32()
dsfmt_randui64() = uint64(dsfmt_randui32()) | (uint64(dsfmt_randui32())<<32)
rand(::GlobalRNG, ::Type{Uint32}) = dsfmt_gv_genrand_uint32()
rand(r::MersenneTwister, ::Type{Uint32}) = dsfmt_genrand_uint32(r.state)

rand(::Type{Uint8}) = uint8(rand(Uint32))
rand(::Type{Uint16}) = uint16(rand(Uint32))
rand(::Type{Uint32}) = dsfmt_randui32()
rand(::Type{Uint64}) = dsfmt_randui64()
rand(::Type{Uint128}) = uint128(rand(Uint64))<<64 | rand(Uint64)
rand(r::MT, ::Type{Bool}) = bool( rand(r, Uint32) & 1)
rand(r::MT, ::Type{Uint8}) = uint8( rand(r, Uint32))
rand(r::MT, ::Type{Uint16}) = uint16( rand(r, Uint32))
rand(r::MT, ::Type{Uint64}) = uint64( rand(r, Uint32))<<32 | rand(r, Uint32)
rand(r::MT, ::Type{Uint128}) = uint128(rand(r, Uint64))<<64 | rand(r, Uint64)

rand(::Type{Int8}) = int8(rand(Uint8))
rand(::Type{Int16}) = int16(rand(Uint16))
rand(::Type{Int32}) = int32(rand(Uint32))
rand(::Type{Int64}) = int64(rand(Uint64))
rand(::Type{Int128}) = int128(rand(Uint128))
rand{T<:Signed128}(r::MT, ::Type{T}) = convert(T, rand(r, as_unsigned(T)))

# Arrays of random numbers
## random complex values (AbstractRNG)

rand(::Type{Float64}, dims::Dims) = rand!(Array(Float64, dims))
rand(::Type{Float64}, dims::Int...) = rand(Float64, dims)
rand{T<:Real}(r::AbstractRNG, ::Type{Complex{T}}) = complex(rand(r, T), rand(r, T))

rand(dims::Dims) = rand(Float64, dims)
rand(dims::Int...) = rand(Float64, dims)
## Arrays of random numbers (AbstractRNG)

rand(r::AbstractRNG, dims::Dims) = rand!(r, Array(Float64, dims))
rand(r::AbstractRNG, dims::Dims) = rand(r, Float64, dims)
rand(r::AbstractRNG, dims::Int...) = rand(r, dims)

function rand!{T}(A::Array{T})
for i=1:length(A)
A[i] = rand(T)
end
A
end
rand(r::AbstractRNG, T::Type, dims::Dims) = rand!(r, Array(T, dims))
rand(r::AbstractRNG, T::Type, d1::Int, dims::Int...) = rand(r, T, tuple(d1, dims...))
# note: the above method would trigger an ambiguity warning if d1 was not separated out:
# rand(r, ()) would match both this method and rand(r, dims::Dims)
# moreover, a call like rand(r, NotImplementedType()) would be an infinite loop

function rand!(r::AbstractRNG, A::AbstractArray)
function rand!{T}(r::AbstractRNG, A::AbstractArray{T})
for i=1:length(A)
@inbounds A[i] = rand(r)
@inbounds A[i] = rand(r, T)
end
A
end

rand(T::Type, dims::Dims) = rand!(Array(T, dims))
rand{T<:Number}(::Type{T}) = error("no random number generator for type $T; try a more specific type")
rand{T<:Number}(::Type{T}, dims::Int...) = rand(T, dims)


## Generate random integer within a range

# remainder function according to Knuth, where rem_knuth(a, 0) = a
Expand Down Expand Up @@ -231,16 +244,18 @@ end
rand{T}(r::Range{T}, dims::Dims) = rand!(r, Array(T, dims))
rand(r::Range, dims::Int...) = rand(r, dims)

## random BitArrays (AbstractRNG)

## random Bools
rand!(r::AbstractRNG, B::BitArray) = Base.bitarray_rand_fill!(r, B)

rand!(B::BitArray) = Base.bitarray_rand_fill!(B)
randbool(r::AbstractRNG, dims::Dims) = rand!(r, BitArray(dims))
randbool(r::AbstractRNG, dims::Int...) = rand!(r, BitArray(dims))

randbool(dims::Dims) = rand!(BitArray(dims))
randbool(dims::Dims) = rand!(BitArray(dims))
randbool(dims::Int...) = rand!(BitArray(dims))

randbool() = ((dsfmt_randui32() & 1) == 1)
rand(::Type{Bool}) = randbool()
randbool(r::AbstractRNG=GlobalRNG()) = rand(r, Bool)


## randn() - Normally distributed random numbers using Ziggurat algorithm

Expand Down
30 changes: 11 additions & 19 deletions doc/stdlib/base.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3932,43 +3932,35 @@ Random Numbers

Random number generation in Julia uses the `Mersenne Twister library <http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/SFMT/#dSFMT>`_. Julia has a global RNG, which is used by default. Multiple RNGs can be plugged in using the ``AbstractRNG`` object, which can then be used to have multiple streams of random numbers. Currently, only ``MersenneTwister`` is supported.

Most functions related to random generation accept an optional ``AbstractRNG`` as the first argument, ``rng`` , which defaults to the global one if not provided. Morever, some of them accept optionally dimension specifications ``dims...`` (which can be given as a tuple) to generate arrays of random values.

A ``MersenneTwister`` RNG can generate random numbers of the following types: ``Float16, Float32, Float64, Bool, Int16, Uint16, Int32, Uint32, Int64, Uint64, Int128, Uint128`` (or complex numbers or arrays of those types). Random floating point numbers are generated uniformly in [0,1).

.. function:: srand([rng], [seed])

Reseed the random number generator. If a ``seed`` is provided, the RNG will give a reproducible sequence of numbers, otherwise Julia will get entropy from the system. The ``seed`` may be an unsigned integer, a vector of unsigned integers or a filename, in which case the seed is read from a file. If the argument ``rng`` is not provided, the default global RNG is seeded.
Reseed the random number generator. If a ``seed`` is provided, the RNG will give a reproducible sequence of numbers, otherwise Julia will get entropy from the system. The ``seed`` may be a non-negative integer, a vector of ``Uint32`` integers or a filename, in which case the seed is read from a file.

.. function:: MersenneTwister([seed])

Create a ``MersenneTwister`` RNG object. Different RNG objects can have their own seeds, which may be useful for generating different streams of random numbers.

.. function:: rand() -> Float64
.. function:: rand([rng], [t::Type], [dims...])

Generate a ``Float64`` random number uniformly in [0,1)
Generate a random value or an array of random values of the given type, ``t``, which defaults to ``Float64``.

.. function:: rand!([rng], A)

Populate the array A with random number generated from the specified RNG.

.. function:: rand(rng::AbstractRNG, [dims...])

Generate a random ``Float64`` number or array of the size specified by dims, using the specified RNG object. Currently, ``MersenneTwister`` is the only available Random Number Generator (RNG), which may be seeded using srand.

.. function:: rand(dims or [dims...])

Generate a random ``Float64`` array of the size specified by dims

.. function:: rand(t::Type, [dims...])

Generate a random number or array of random numbes of the given type.
Populate the array A with random values.

.. function:: rand(r, [dims...])

Pick a random element or array of random elements from range ``r`` (for example, ``1:n`` or ``0:2:10``).

.. function:: randbool([dims...])
.. function:: randbool([rng], [dims...])

Generate a random boolean value. Optionally, generate an array of random boolean values.
Generate a random boolean value. Optionally, generate a ``BitArray`` of random boolean values.

.. function:: randn([rng], dims or [dims...])
.. function:: randn([rng], [dims...])

Generate a normally-distributed random number with mean 0 and standard deviation 1. Optionally generate an array of normally-distributed random numbers.

Expand Down
7 changes: 6 additions & 1 deletion test/random.jl
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,17 @@ srand(0); rand(); x = rand(384);
# Try a seed larger than 2^32
@test rand(MersenneTwister(5294967296)) == 0.3498809918210497

# Test array filling, Issue #7643
# Test array filling, Issues #7643, #8360
@test rand(MersenneTwister(0), 1) == [0.8236475079774124]
A = zeros(2, 2)
rand!(MersenneTwister(0), A)
@test A == [0.8236475079774124 0.16456579813368521;
0.9103565379264364 0.17732884646626457]
@test rand(MersenneTwister(0), Int64, 1) == [172014471070449746]
A = zeros(Int64, 2, 2)
rand!(MersenneTwister(0), A)
@test A == [ 172014471070449746 -193283627354378518;
-4679130500738884555 -9008350441255501549]

# randn
@test randn(MersenneTwister(42)) == -0.5560268761463861
Expand Down