diff --git a/base/bitarray.jl b/base/bitarray.jl index 0c5dfbf6e6198..d95e38f131e90 100644 --- a/base/bitarray.jl +++ b/base/bitarray.jl @@ -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 diff --git a/base/int.jl b/base/int.jl index fa3d5f81b1875..75febe5b638ba 100644 --- a/base/int.jl +++ b/base/int.jl @@ -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) @@ -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))) @@ -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 diff --git a/base/random.jl b/base/random.jl index 789015dfe4e23..40d672f0717cc 100644 --- a/base/random.jl +++ b/base/random.jl @@ -1,6 +1,6 @@ module Random -using Base.dSFMT +using Base: dSFMT, Integer128, Signed128, as_unsigned export srand, rand, rand!, @@ -13,30 +13,46 @@ 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()) @@ -44,26 +60,18 @@ function srand() 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()) @@ -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 @@ -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 diff --git a/doc/stdlib/base.rst b/doc/stdlib/base.rst index bfc2ca46c3e65..9ab757b0464ab 100644 --- a/doc/stdlib/base.rst +++ b/doc/stdlib/base.rst @@ -3932,43 +3932,35 @@ Random Numbers Random number generation in Julia uses the `Mersenne Twister library `_. 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. diff --git a/test/random.jl b/test/random.jl index e0d6d1318622f..d2260128e4d96 100644 --- a/test/random.jl +++ b/test/random.jl @@ -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