Skip to content

Commit

Permalink
add more methods rand(rng, ...)
Browse files Browse the repository at this point in the history
And similarly for rand!, randbool.
Fixes #8360.
  • Loading branch information
rfourquet committed Sep 17, 2014
1 parent fad8b9d commit 8f8e43d
Show file tree
Hide file tree
Showing 5 changed files with 95 additions and 71 deletions.
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)
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
98 changes: 52 additions & 46 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 @@ -23,6 +23,25 @@ type MersenneTwister <: AbstractRNG
MersenneTwister() = MersenneTwister(0)
end

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

__init__() = srand()
Expand Down Expand Up @@ -97,64 +116,49 @@ srand(r::MersenneTwister, filename::String, n::Integer=4) = srand(r, make_seed(f

## random floating point values

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

rand(::Type{Float32}) = float32(rand())
rand(::Type{Float16}) = float16(rand())

rand{T<:Real}(::Type{Complex{T}}) = complex(rand(T),rand(T))
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 @@ -240,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 a non-negative integer, a vector of ``Uint32`` 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

0 comments on commit 8f8e43d

Please sign in to comment.