diff --git a/Cargo.toml b/Cargo.toml index 56ab26af..1035bf6a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -25,8 +25,7 @@ cfg-if = "1.0.0" ndarray = { version = "0.15", optional = true } num-traits = "0.2.12" num = "0.4" -rand = { version = "0.8.5", default-features = false, features = ["small_rng"] } -rand_distr = { version = "0.4", optional = true } +rand = { version = "0.10.1", default-features = false, features = ["alloc"] } serde = { version = "1", features = ["derive"], optional = true } ordered-float = "5.1.0" @@ -37,13 +36,13 @@ typetag = { version = "0.2", optional = true } default = [] serde = ["dep:serde", "dep:typetag"] ndarray-bindings = ["dep:ndarray"] -datasets = ["dep:rand_distr", "std_rand", "serde"] -std_rand = ["rand/std_rng", "rand/std"] +datasets = ["std_rand", "serde"] +std_rand = ["rand/std_rng", "rand/std", "rand/thread_rng"] # used by wasm32-unknown-unknown for in-browser usage -js = ["getrandom/js"] +js = ["getrandom/wasm_js"] [target.'cfg(target_arch = "wasm32")'.dependencies] -getrandom = { version = "0.2.8", optional = true } +getrandom = { version = "0.4", optional = true } [target.'cfg(all(target_arch = "wasm32", not(target_os = "wasi")))'.dev-dependencies] wasm-bindgen-test = "0.3" diff --git a/src/cluster/kmeans.rs b/src/cluster/kmeans.rs index 2fade68f..b81ffd7e 100644 --- a/src/cluster/kmeans.rs +++ b/src/cluster/kmeans.rs @@ -55,7 +55,7 @@ use std::fmt::Debug; use std::marker::PhantomData; -use rand::Rng; +use rand::RngExt; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; @@ -356,7 +356,7 @@ impl, Y: Array1> KMeans let (n, _) = data.shape(); let mut y = vec![0; n]; let mut centroid: Vec = data - .get_row(rng.gen_range(0..n)) + .get_row(rng.random_range(0..n)) .iterator(0) .cloned() .collect(); @@ -382,7 +382,7 @@ impl, Y: Array1> KMeans for i in d.iter() { sum += *i; } - let cutoff = rng.gen::() * sum; + let cutoff = rng.random::() * sum; let mut cost = 0f64; let mut index = 0; while index < n { diff --git a/src/dataset/generator.rs b/src/dataset/generator.rs index f8e59443..bf4c7741 100644 --- a/src/dataset/generator.rs +++ b/src/dataset/generator.rs @@ -1,38 +1,47 @@ //! # Dataset Generators //! -use rand::distributions::Uniform; -use rand::prelude::*; -use rand_distr::Normal; +use rand::distr::Distribution; +use rand::distr::Uniform; use crate::dataset::Dataset; +/// Sample from N(mean, std) via Box-Muller transform using only rand 0.10 +#[inline] +fn sample_normal(mean: f32, std: f32, rng: &mut impl rand::Rng) -> f32 { + let unit = Uniform::new(f32::EPSILON, 1.0f32).unwrap(); + let u1 = unit.sample(rng); + let u2 = unit.sample(rng); + let z = (-2.0 * u1.ln()).sqrt() * (2.0 * std::f32::consts::PI * u2).cos(); + mean + std * z +} + /// Generate `num_centers` clusters of normally distributed points pub fn make_blobs( num_samples: usize, num_features: usize, num_centers: usize, ) -> Dataset { - let center_box = Uniform::from(-10.0..10.0); - let cluster_std = 1.0; - let mut centers: Vec>> = Vec::with_capacity(num_centers); + let center_box = Uniform::new(-10.0f32, 10.0f32).expect("Invalid uniform range"); + let cluster_std = 1.0f32; + let mut rng = rand::rng(); - let mut rng = rand::thread_rng(); - for _ in 0..num_centers { - centers.push( + // Pre-compute cluster centers (one mean per feature per cluster) + let centers: Vec> = (0..num_centers) + .map(|_| { (0..num_features) - .map(|_| Normal::new(center_box.sample(&mut rng), cluster_std).unwrap()) - .collect(), - ); - } + .map(|_| center_box.sample(&mut rng)) + .collect() + }) + .collect(); let mut y: Vec = Vec::with_capacity(num_samples); - let mut x: Vec = Vec::with_capacity(num_samples); + let mut x: Vec = Vec::with_capacity(num_samples * num_features); for i in 0..num_samples { let label = i % num_centers; y.push(label as f32); for j in 0..num_features { - x.push(centers[label][j].sample(&mut rng)); + x.push(sample_normal(centers[label][j], cluster_std, &mut rng)); } } @@ -59,21 +68,20 @@ pub fn make_circles(num_samples: usize, factor: f32, noise: f32) -> Dataset = Vec::with_capacity(num_samples * 2); let mut y: Vec = Vec::with_capacity(num_samples); for v in linspace_out { - x.push(v.cos() + noise.sample(&mut rng)); - x.push(v.sin() + noise.sample(&mut rng)); + x.push(v.cos() + sample_normal(0.0, noise, &mut rng)); + x.push(v.sin() + sample_normal(0.0, noise, &mut rng)); y.push(0.0); } for v in linspace_in { - x.push(v.cos() * factor + noise.sample(&mut rng)); - x.push(v.sin() * factor + noise.sample(&mut rng)); + x.push(v.cos() * factor + sample_normal(0.0, noise, &mut rng)); + x.push(v.sin() * factor + sample_normal(0.0, noise, &mut rng)); y.push(1.0); } @@ -96,21 +104,20 @@ pub fn make_moons(num_samples: usize, noise: f32) -> Dataset { let linspace_out = linspace(0.0, std::f32::consts::PI, num_samples_out); let linspace_in = linspace(0.0, std::f32::consts::PI, num_samples_in); - let noise = Normal::new(0.0, noise).unwrap(); - let mut rng = rand::thread_rng(); + let mut rng = rand::rng(); let mut x: Vec = Vec::with_capacity(num_samples * 2); let mut y: Vec = Vec::with_capacity(num_samples); for v in linspace_out { - x.push(v.cos() + noise.sample(&mut rng)); - x.push(v.sin() + noise.sample(&mut rng)); + x.push(v.cos() + sample_normal(0.0, noise, &mut rng)); + x.push(v.sin() + sample_normal(0.0, noise, &mut rng)); y.push(0.0); } for v in linspace_in { - x.push(1.0 - v.cos() + noise.sample(&mut rng)); - x.push(1.0 - v.sin() + noise.sample(&mut rng) - 0.5); + x.push(1.0 - v.cos() + sample_normal(0.0, noise, &mut rng)); + x.push(1.0 - v.sin() + sample_normal(0.0, noise, &mut rng) - 0.5); y.push(1.0); } diff --git a/src/ensemble/base_forest_regressor.rs b/src/ensemble/base_forest_regressor.rs index 4209034c..223a8b90 100644 --- a/src/ensemble/base_forest_regressor.rs +++ b/src/ensemble/base_forest_regressor.rs @@ -1,4 +1,4 @@ -use rand::Rng; +use rand::RngExt; use std::fmt::Debug; #[cfg(feature = "serde")] @@ -209,10 +209,10 @@ impl, Y: Array1 result / TY::from(n_trees).unwrap() } - fn sample_with_replacement(nrows: usize, rng: &mut impl Rng) -> Vec { + fn sample_with_replacement(nrows: usize, rng: &mut impl rand::Rng) -> Vec { let mut samples = vec![0; nrows]; for _ in 0..nrows { - let xi = rng.gen_range(0..nrows); + let xi = rng.random_range(0..nrows); samples[xi] += 1; } samples diff --git a/src/ensemble/random_forest_classifier.rs b/src/ensemble/random_forest_classifier.rs index f4e8db3c..0f86a4df 100644 --- a/src/ensemble/random_forest_classifier.rs +++ b/src/ensemble/random_forest_classifier.rs @@ -45,7 +45,7 @@ //! //! //! -use rand::Rng; +use rand::RngExt; use std::default::Default; use std::fmt::Debug; @@ -587,7 +587,11 @@ impl, Y: Array1 Vec { + fn sample_with_replacement( + y: &[usize], + num_classes: usize, + rng: &mut impl rand::Rng, + ) -> Vec { let class_weight = vec![1.; num_classes]; let nrows = y.len(); let mut samples = vec![0; nrows]; @@ -603,7 +607,7 @@ impl, Y: Array1 f64 { - use rand::Rng; + use rand::RngExt; let mut rng = get_rng_impl(None); - rng.gen() + rng.random() } fn two() -> Self { @@ -98,9 +98,9 @@ impl FloatNumber for f32 { } fn rand() -> f32 { - use rand::Rng; + use rand::RngExt; let mut rng = get_rng_impl(None); - rng.gen() + rng.random() } fn two() -> Self { diff --git a/src/numbers/realnum.rs b/src/numbers/realnum.rs index 8ef71555..8484f295 100644 --- a/src/numbers/realnum.rs +++ b/src/numbers/realnum.rs @@ -3,7 +3,7 @@ //! This module defines real number and some useful functions that are used in [Linear Algebra](../../linalg/index.html) module. use rand::rngs::SmallRng; -use rand::{Rng, SeedableRng}; +use rand::{RngExt, SeedableRng}; use num_traits::Float; @@ -69,10 +69,8 @@ impl RealNumber for f64 { fn rand() -> f64 { let mut small_rng = get_rng_impl(None); - let mut rngs: Vec = (0..3) - .map(|_| SmallRng::from_rng(&mut small_rng).unwrap()) - .collect(); - rngs[0].gen::() + let mut rngs: Vec = (0..3).map(|_| SmallRng::from_rng(&mut small_rng)).collect(); + rngs[0].random::() } fn two() -> Self { @@ -118,10 +116,8 @@ impl RealNumber for f32 { fn rand() -> f32 { let mut small_rng = get_rng_impl(None); - let mut rngs: Vec = (0..3) - .map(|_| SmallRng::from_rng(&mut small_rng).unwrap()) - .collect(); - rngs[0].gen::() + let mut rngs: Vec = (0..3).map(|_| SmallRng::from_rng(&mut small_rng)).collect(); + rngs[0].random::() } fn two() -> Self { diff --git a/src/rand_custom.rs b/src/rand_custom.rs index 936ec9e9..957fe8c0 100644 --- a/src/rand_custom.rs +++ b/src/rand_custom.rs @@ -11,8 +11,11 @@ pub fn get_rng_impl(seed: Option) -> RngImpl { None => { cfg_if::cfg_if! { if #[cfg(feature = "std_rand")] { - use rand::RngCore; - RngImpl::seed_from_u64(rand::thread_rng().next_u64()) + use rand::Rng; + // FIX: thread_rng() deprecated in rand 0.9 → use rng() + // FIX: rand 0.10 no longer re-exports RngCore at root; + // import rand::Rng (supertrait) instead so next_u64() resolves + RngImpl::seed_from_u64(rand::rng().next_u64()) } else { // no std_random feature build, use getrandom #[cfg(feature = "js")] diff --git a/src/readers/io_testing.rs b/src/readers/io_testing.rs index cb0b4b0f..66dc86f4 100644 --- a/src/readers/io_testing.rs +++ b/src/readers/io_testing.rs @@ -1,7 +1,7 @@ //! This module contains functionality to test IO. It has both functions that write //! to the file-system for end-to-end tests, but also abstractions to avoid this by //! reading from strings instead. -use rand::distributions::{Alphanumeric, DistString}; +use rand::distr::{Alphanumeric, SampleString}; use std::fs; use std::io::Bytes; use std::io::Read; @@ -16,7 +16,7 @@ pub struct TemporaryTextFile { impl TemporaryTextFile { pub fn new(contents: &str) -> std::io::Result { let test_text_file = TemporaryTextFile { - random_path: Alphanumeric.sample_string(&mut rand::thread_rng(), 16), + random_path: Alphanumeric.sample_string(&mut rand::rng(), 16), }; string_to_file(contents, &test_text_file.random_path)?; Ok(test_text_file) diff --git a/src/tree/base_tree_regressor.rs b/src/tree/base_tree_regressor.rs index 87288947..f84ae7e9 100644 --- a/src/tree/base_tree_regressor.rs +++ b/src/tree/base_tree_regressor.rs @@ -4,7 +4,7 @@ use std::fmt::Debug; use std::marker::PhantomData; use rand::seq::SliceRandom; -use rand::Rng; +use rand::RngExt; #[cfg(feature = "serde")] use serde::{Deserialize, Serialize}; @@ -292,7 +292,7 @@ impl, Y: Array1> &mut self, visitor: &mut NodeVisitor<'_, TX, TY, X, Y>, mtry: usize, - rng: &mut impl Rng, + rng: &mut impl rand::Rng, ) -> bool { let (_, n_attr) = visitor.x.shape(); @@ -336,7 +336,7 @@ impl, Y: Array1> sum: f64, parent_gain: f64, j: usize, - rng: &mut impl Rng, + rng: &mut impl rand::Rng, ) { let (min_val, max_val) = { let mut min_opt = None; @@ -363,7 +363,7 @@ impl, Y: Array1> return; } - let split_value = rng.gen_range(min_val.to_f64().unwrap()..max_val.to_f64().unwrap()); + let split_value = rng.random_range(min_val.to_f64().unwrap()..max_val.to_f64().unwrap()); let mut true_sum = 0f64; let mut true_count = 0; @@ -476,7 +476,7 @@ impl, Y: Array1> mut visitor: NodeVisitor<'a, TX, TY, X, Y>, mtry: usize, visitor_queue: &mut LinkedList>, - rng: &mut impl Rng, + rng: &mut impl rand::Rng, ) -> bool { let (n, _) = visitor.x.shape(); let mut tc = 0;