forked from Rust-related/RustPython
Implement _random using the MT19937 algorithm
This commit is contained in:
11
Cargo.lock
generated
11
Cargo.lock
generated
@@ -1245,15 +1245,6 @@ dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_distr"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "96977acbdd3a6576fb1d27391900035bf3863d4a16422973a409b488cf29ffb2"
|
||||
dependencies = [
|
||||
"rand 0.7.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_hc"
|
||||
version = "0.1.0"
|
||||
@@ -1539,7 +1530,7 @@ dependencies = [
|
||||
"paste",
|
||||
"pwd",
|
||||
"rand 0.7.3",
|
||||
"rand_distr",
|
||||
"rand_core 0.5.1",
|
||||
"regex",
|
||||
"result-like",
|
||||
"rustc_version_runtime",
|
||||
|
||||
@@ -30,8 +30,8 @@ num-traits = "0.2.8"
|
||||
num-integer = "0.1.41"
|
||||
num-rational = "0.2.2"
|
||||
num-iter = "0.1.39"
|
||||
rand = { version = "0.7", features = ["small_rng"] }
|
||||
rand_distr = "0.2"
|
||||
rand = "0.7"
|
||||
rand_core = "0.5"
|
||||
getrandom = "0.1"
|
||||
log = "0.4"
|
||||
rustpython-derive = {path = "../derive", version = "0.1.1"}
|
||||
|
||||
@@ -3,22 +3,60 @@
|
||||
use std::cell::RefCell;
|
||||
|
||||
use num_bigint::{BigInt, Sign};
|
||||
use num_traits::Signed;
|
||||
use rand::RngCore;
|
||||
|
||||
use rand::distributions::Distribution;
|
||||
use rand::{RngCore, SeedableRng};
|
||||
use rand::rngs::SmallRng;
|
||||
use rand_distr::Normal;
|
||||
|
||||
use crate::function::OptionalArg;
|
||||
use crate::function::OptionalOption;
|
||||
use crate::obj::objint::PyIntRef;
|
||||
use crate::obj::objtype::PyClassRef;
|
||||
use crate::pyobject::{PyClassImpl, PyObjectRef, PyRef, PyValue, PyResult};
|
||||
use crate::pyobject::{PyClassImpl, PyObjectRef, PyRef, PyResult, PyValue};
|
||||
use crate::VirtualMachine;
|
||||
|
||||
use crate::vm::VirtualMachine;
|
||||
mod mersenne;
|
||||
|
||||
#[derive(Debug)]
|
||||
enum PyRng {
|
||||
Std(rand::rngs::ThreadRng),
|
||||
MT(mersenne::MT19937),
|
||||
}
|
||||
|
||||
impl Default for PyRng {
|
||||
fn default() -> Self {
|
||||
PyRng::Std(rand::thread_rng())
|
||||
}
|
||||
}
|
||||
|
||||
impl RngCore for PyRng {
|
||||
fn next_u32(&mut self) -> u32 {
|
||||
match self {
|
||||
Self::Std(s) => s.next_u32(),
|
||||
Self::MT(m) => m.next_u32(),
|
||||
}
|
||||
}
|
||||
fn next_u64(&mut self) -> u64 {
|
||||
match self {
|
||||
Self::Std(s) => s.next_u64(),
|
||||
Self::MT(m) => m.next_u64(),
|
||||
}
|
||||
}
|
||||
fn fill_bytes(&mut self, dest: &mut [u8]) {
|
||||
match self {
|
||||
Self::Std(s) => s.fill_bytes(dest),
|
||||
Self::MT(m) => m.fill_bytes(dest),
|
||||
}
|
||||
}
|
||||
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand::Error> {
|
||||
match self {
|
||||
Self::Std(s) => s.try_fill_bytes(dest),
|
||||
Self::MT(m) => m.try_fill_bytes(dest),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[pyclass(name = "Random")]
|
||||
#[derive(Debug)]
|
||||
struct PyRandom {
|
||||
rng: RefCell<SmallRng>
|
||||
rng: RefCell<PyRng>,
|
||||
}
|
||||
|
||||
impl PyValue for PyRandom {
|
||||
@@ -27,86 +65,74 @@ impl PyValue for PyRandom {
|
||||
}
|
||||
}
|
||||
|
||||
#[pyimpl]
|
||||
#[pyimpl(flags(BASETYPE))]
|
||||
impl PyRandom {
|
||||
#[pyslot(new)]
|
||||
fn new(cls: PyClassRef, vm: &VirtualMachine) -> PyResult<PyRef<Self>> {
|
||||
PyRandom {
|
||||
rng: RefCell::new(SmallRng::from_entropy())
|
||||
}.into_ref_with_type(vm, cls)
|
||||
}
|
||||
|
||||
#[pymethod]
|
||||
fn seed(&self, n: Option<usize>, vm: &VirtualMachine) -> PyResult {
|
||||
let rng = match n {
|
||||
None => SmallRng::from_entropy(),
|
||||
Some(n) => {
|
||||
let seed = n as u64;
|
||||
SmallRng::seed_from_u64(seed)
|
||||
}
|
||||
};
|
||||
|
||||
*self.rng.borrow_mut() = rng;
|
||||
|
||||
Ok(vm.ctx.none())
|
||||
rng: RefCell::new(PyRng::default()),
|
||||
}
|
||||
.into_ref_with_type(vm, cls)
|
||||
}
|
||||
|
||||
#[pymethod]
|
||||
fn getrandbits(&self, k: usize, vm: &VirtualMachine) -> PyResult {
|
||||
let bytes = (k - 1) / 8 + 1;
|
||||
let mut bytearray = vec![0u8; bytes];
|
||||
self.rng.borrow_mut().fill_bytes(&mut bytearray);
|
||||
fn random(&self) -> f64 {
|
||||
gen_res53(&mut *self.rng.borrow_mut())
|
||||
}
|
||||
|
||||
let bits = bytes % 8;
|
||||
if bits > 0 {
|
||||
bytearray[0] >>= 8 - bits;
|
||||
#[pymethod]
|
||||
fn seed(&self, n: OptionalOption<PyIntRef>) {
|
||||
let new_rng = match n.flat_option() {
|
||||
None => PyRng::default(),
|
||||
Some(n) => {
|
||||
let (_, mut key) = n.as_bigint().abs().to_u32_digits();
|
||||
if cfg!(target_endian = "big") {
|
||||
key.reverse();
|
||||
}
|
||||
PyRng::MT(mersenne::MT19937::new_with_slice_seed(&key))
|
||||
}
|
||||
};
|
||||
|
||||
*self.rng.borrow_mut() = new_rng;
|
||||
}
|
||||
|
||||
#[pymethod]
|
||||
fn getrandbits(&self, mut k: usize) -> BigInt {
|
||||
let mut rng = self.rng.borrow_mut();
|
||||
|
||||
let mut gen_u32 = |k| rng.next_u32() >> (32 - k) as u32;
|
||||
|
||||
if k <= 32 {
|
||||
return gen_u32(k).into();
|
||||
}
|
||||
|
||||
println!("{:?}", k);
|
||||
println!("{:?}", bytearray);
|
||||
|
||||
let result = BigInt::from_bytes_be(Sign::Plus, &bytearray);
|
||||
Ok(vm.ctx.new_bigint(&result))
|
||||
let words = (k - 1) / 8 + 1;
|
||||
let mut wordarray = vec![0u32; words];
|
||||
|
||||
let it = wordarray.iter_mut();
|
||||
#[cfg(target_endian = "big")]
|
||||
let it = it.rev();
|
||||
for word in it {
|
||||
*word = gen_u32(k);
|
||||
k -= 32;
|
||||
}
|
||||
|
||||
BigInt::from_slice(Sign::NoSign, &wordarray)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn make_module(vm: &VirtualMachine) -> PyObjectRef {
|
||||
let ctx = &vm.ctx;
|
||||
let random_type = PyRandom::make_class(ctx);
|
||||
|
||||
py_module!(vm, "_random", {
|
||||
"Random" => random_type,
|
||||
"gauss" => ctx.new_function(random_normalvariate), // TODO: is this the same?
|
||||
"normalvariate" => ctx.new_function(random_normalvariate),
|
||||
"random" => ctx.new_function(random_random),
|
||||
// "weibull", ctx.new_function(random_weibullvariate),
|
||||
"Random" => PyRandom::make_class(ctx),
|
||||
})
|
||||
}
|
||||
|
||||
fn random_normalvariate(mu: f64, sigma: f64, vm: &VirtualMachine) -> PyResult<f64> {
|
||||
let normal = Normal::new(mu, sigma).map_err(|rand_err| {
|
||||
vm.new_exception_msg(
|
||||
vm.ctx.exceptions.arithmetic_error.clone(),
|
||||
format!("invalid normal distribution: {:?}", rand_err),
|
||||
)
|
||||
})?;
|
||||
let value = normal.sample(&mut rand::thread_rng());
|
||||
Ok(value)
|
||||
// taken from the translated mersenne twister
|
||||
/* generates a random number on [0,1) with 53-bit resolution*/
|
||||
fn gen_res53<R: RngCore>(rng: &mut R) -> f64 {
|
||||
let a = rng.next_u32() >> 5;
|
||||
let b = rng.next_u32() >> 6;
|
||||
(a as f64 * 67108864.0 + b as f64) * (1.0 / 9007199254740992.0)
|
||||
}
|
||||
|
||||
fn random_random(_vm: &VirtualMachine) -> f64 {
|
||||
rand::random()
|
||||
}
|
||||
|
||||
/*
|
||||
* TODO: enable this function:
|
||||
fn random_weibullvariate(vm: &VirtualMachine, args: PyFuncArgs) -> PyResult {
|
||||
arg_check!(vm, args, required = [(alpha, Some(vm.ctx.float_type())), (beta, Some(vm.ctx.float_type()))]);
|
||||
let alpha = objfloat::get_value(alpha);
|
||||
let beta = objfloat::get_value(beta);
|
||||
let weibull = Weibull::new(alpha, beta);
|
||||
let value = weibull.sample(&mut rand::thread_rng());
|
||||
let py_value = vm.ctx.new_float(value);
|
||||
Ok(py_value)
|
||||
}
|
||||
*/
|
||||
/* These real versions are due to Isaku Wada, 2002/01/09 added */
|
||||
|
||||
202
vm/src/stdlib/random/mersenne.rs
Normal file
202
vm/src/stdlib/random/mersenne.rs
Normal file
@@ -0,0 +1,202 @@
|
||||
/*
|
||||
A C-program for MT19937, with initialization improved 2002/1/26.
|
||||
Coded by Takuji Nishimura and Makoto Matsumoto.
|
||||
|
||||
Before using, initialize the state by using init_genrand(seed)
|
||||
or init_by_array(init_key, key_length).
|
||||
|
||||
Copyright (C) 1997 - 2002, Makoto Matsumoto and Takuji Nishimura,
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
3. The names of its contributors may not be used to endorse or promote
|
||||
products derived from this software without specific prior written
|
||||
permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
|
||||
Any feedback is very welcome.
|
||||
http://www.math.sci.hiroshima-u.ac.jp/~m-mat/MT/emt.html
|
||||
email: m-mat @ math.sci.hiroshima-u.ac.jp (remove space)
|
||||
*/
|
||||
|
||||
// this was translated from c; all rights go to copyright holders listed above
|
||||
// https://gist.github.com/coolreader18/b56d510f1b0551d2954d74ad289f7d2e
|
||||
|
||||
/* Period parameters */
|
||||
const N: usize = 624;
|
||||
const M: usize = 397;
|
||||
const MATRIX_A: u32 = 0x9908b0dfu32; /* constant vector a */
|
||||
const UPPER_MASK: u32 = 0x80000000u32; /* most significant w-r bits */
|
||||
const LOWER_MASK: u32 = 0x7fffffffu32; /* least significant r bits */
|
||||
|
||||
pub struct MT19937 {
|
||||
mt: [u32; N], /* the array for the state vector */
|
||||
mti: usize, /* mti==N+1 means mt[N] is not initialized */
|
||||
}
|
||||
impl Default for MT19937 {
|
||||
fn default() -> Self {
|
||||
MT19937 {
|
||||
mt: [0; N],
|
||||
mti: N + 1,
|
||||
}
|
||||
}
|
||||
}
|
||||
impl std::fmt::Debug for MT19937 {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
f.pad("MT19937")
|
||||
}
|
||||
}
|
||||
|
||||
impl MT19937 {
|
||||
pub fn new_with_slice_seed(init_key: &[u32]) -> Self {
|
||||
let mut state = Self::default();
|
||||
state.seed_slice(init_key);
|
||||
state
|
||||
}
|
||||
|
||||
/* initializes self.mt[N] with a seed */
|
||||
fn seed(&mut self, s: u32) {
|
||||
self.mt[0] = s & 0xffffffffu32;
|
||||
self.mti = 1;
|
||||
while self.mti < N {
|
||||
self.mt[self.mti] = 1812433253u32
|
||||
.wrapping_mul(self.mt[self.mti - 1] ^ (self.mt[self.mti - 1] >> 30))
|
||||
+ self.mti as u32;
|
||||
/* See Knuth TAOCP Vol2. 3rd Ed. P.106 for multiplier. */
|
||||
/* In the previous versions, MSBs of the seed affect */
|
||||
/* only MSBs of the array self.mt[]. */
|
||||
/* 2002/01/09 modified by Makoto Matsumoto */
|
||||
self.mt[self.mti] &= 0xffffffffu32;
|
||||
/* for >32 bit machines */
|
||||
self.mti += 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* initialize by an array with array-length */
|
||||
/* init_key is the array for initializing keys */
|
||||
/* key_length is its length */
|
||||
/* slight change for C++, 2004/2/26 */
|
||||
pub fn seed_slice(&mut self, init_key: &[u32]) {
|
||||
let mut i;
|
||||
let mut j;
|
||||
let mut k;
|
||||
self.seed(19650218);
|
||||
i = 1;
|
||||
j = 0;
|
||||
k = if N > init_key.len() {
|
||||
N
|
||||
} else {
|
||||
init_key.len()
|
||||
};
|
||||
while k != 0 {
|
||||
self.mt[i] = (self.mt[i]
|
||||
^ ((self.mt[i - 1] ^ (self.mt[i - 1] >> 30)).wrapping_mul(1664525u32)))
|
||||
+ init_key[j]
|
||||
+ j as u32; /* non linear */
|
||||
self.mt[i] &= 0xffffffffu32; /* for WORDSIZE > 32 machines */
|
||||
i += 1;
|
||||
j += 1;
|
||||
if i >= N {
|
||||
self.mt[0] = self.mt[N - 1];
|
||||
i = 1;
|
||||
}
|
||||
if j >= init_key.len() {
|
||||
j = 0;
|
||||
}
|
||||
k -= 1;
|
||||
}
|
||||
k = N - 1;
|
||||
while k != 0 {
|
||||
self.mt[i] = (self.mt[i]
|
||||
^ ((self.mt[i - 1] ^ (self.mt[i - 1] >> 30)).wrapping_mul(1566083941u32)))
|
||||
- i as u32; /* non linear */
|
||||
self.mt[i] &= 0xffffffffu32; /* for WORDSIZE > 32 machines */
|
||||
i += 1;
|
||||
if i >= N {
|
||||
self.mt[0] = self.mt[N - 1];
|
||||
i = 1;
|
||||
}
|
||||
k -= 1;
|
||||
}
|
||||
|
||||
self.mt[0] = 0x80000000u32; /* MSB is 1; assuring non-zero initial array */
|
||||
}
|
||||
|
||||
/* generates a random number on [0,0xffffffff]-interval */
|
||||
fn gen_u32(&mut self) -> u32 {
|
||||
let mut y: u32;
|
||||
let mag01 = |x| if (x & 0x1) == 1 { MATRIX_A } else { 0 };
|
||||
/* mag01[x] = x * MATRIX_A for x=0,1 */
|
||||
|
||||
if self.mti >= N {
|
||||
/* generate N words at one time */
|
||||
|
||||
if self.mti == N + 1
|
||||
/* if seed() has not been called, */
|
||||
{
|
||||
self.seed(5489u32);
|
||||
} /* a default initial seed is used */
|
||||
|
||||
for kk in 0..N - M {
|
||||
y = (self.mt[kk] & UPPER_MASK) | (self.mt[kk + 1] & LOWER_MASK);
|
||||
self.mt[kk] = self.mt[kk + M] ^ (y >> 1) ^ mag01(y);
|
||||
}
|
||||
for kk in N - M..N - 1 {
|
||||
y = (self.mt[kk] & UPPER_MASK) | (self.mt[kk + 1] & LOWER_MASK);
|
||||
self.mt[kk] = self.mt[kk.wrapping_add(M.wrapping_sub(N))] ^ (y >> 1) ^ mag01(y);
|
||||
}
|
||||
y = (self.mt[N - 1] & UPPER_MASK) | (self.mt[0] & LOWER_MASK);
|
||||
self.mt[N - 1] = self.mt[M - 1] ^ (y >> 1) ^ mag01(y);
|
||||
|
||||
self.mti = 0;
|
||||
}
|
||||
|
||||
y = self.mt[self.mti];
|
||||
self.mti += 1;
|
||||
|
||||
/* Tempering */
|
||||
y ^= y >> 11;
|
||||
y ^= (y << 7) & 0x9d2c5680u32;
|
||||
y ^= (y << 15) & 0xefc60000u32;
|
||||
y ^= y >> 18;
|
||||
|
||||
y
|
||||
}
|
||||
}
|
||||
|
||||
impl rand::RngCore for MT19937 {
|
||||
fn next_u32(&mut self) -> u32 {
|
||||
self.gen_u32()
|
||||
}
|
||||
fn next_u64(&mut self) -> u64 {
|
||||
rand_core::impls::next_u64_via_u32(self)
|
||||
}
|
||||
fn fill_bytes(&mut self, dest: &mut [u8]) {
|
||||
rand_core::impls::fill_bytes_via_next(self, dest)
|
||||
}
|
||||
fn try_fill_bytes(&mut self, dest: &mut [u8]) -> Result<(), rand::Error> {
|
||||
Ok(self.fill_bytes(dest))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user