1use std::cell::RefCell;
13use std::sync::OnceLock;
14
15use crate::ops::{add, divide, erfinv, minimum, multiply, slice, subtract};
16use crate::primitive::Primitive;
17use crate::{Array, DType, Error, Result, SliceArg, bf16, f16};
18
19pub fn key(seed: u64) -> Array {
21 Array::from_slice(&[(seed >> 32) as u32, seed as u32], &[2]).expect("two elements")
22}
23
24fn time_seed() -> u64 {
25 static SEED: OnceLock<u64> = OnceLock::new();
26 *SEED.get_or_init(|| {
27 std::time::SystemTime::now()
28 .duration_since(std::time::UNIX_EPOCH)
29 .map_or(0, |d| d.as_millis() as u64)
30 })
31}
32
33thread_local!(static SEQUENCE: RefCell<Option<Array>> = const { RefCell::new(None) });
34
35pub fn seed(seed: u64) {
37 SEQUENCE.set(Some(key(seed)));
38}
39
40pub fn next_key() -> Result<Array> {
44 let current = SEQUENCE.with_borrow_mut(|k| k.get_or_insert_with(|| key(time_seed())).clone());
45 let (keep, out) = split_pair(¤t)?;
46 SEQUENCE.set(Some(keep));
47 Ok(out)
48}
49
50fn key_or_next(key: Option<&Array>) -> Result<Array> {
51 match key {
52 Some(k) => Ok(k.clone()),
53 None => next_key(),
54 }
55}
56
57pub fn bits(shape: &[usize], width: usize, key: Option<&Array>) -> Result<Array> {
59 let key = key_or_next(key)?;
60 if key.dtype() != DType::U32 || key.shape() != [2] {
61 return Err(Error::InvalidArgument(format!(
62 "bits: expected a uint32 key of shape [2], got {} {:?}",
63 key.dtype(),
64 key.shape()
65 )));
66 }
67 let width = u8::try_from(width)
68 .map_err(|_| Error::InvalidArgument(format!("bits: width {width} not in {{1, 2, 4}}")))?;
69 let primitive = Primitive::RandomBits {
70 shape: shape.to_vec(),
71 width,
72 };
73 Ok(Array::from_op(primitive, vec![key])?.swap_remove(0))
74}
75
76pub fn split(key: &Array, num: usize) -> Result<Array> {
78 bits(&[num, 2], 4, Some(key))
79}
80
81pub fn split_pair(key: &Array) -> Result<(Array, Array)> {
83 let both = split(key, 2)?;
84 let row = |i: isize| {
85 let args = [SliceArg::new(Some(i), Some(i + 1), 1), SliceArg::full()];
86 slice(&both, &args)?.reshape(&[2])
87 };
88 Ok((row(0)?, row(1)?))
89}
90
91fn below_one(dtype: DType) -> Result<f32> {
93 Ok(match dtype {
94 DType::F32 => f32::from_bits(1.0f32.to_bits() - 1),
95 DType::F16 => f16::from_bits(f16::ONE.to_bits() - 1).to_f32(),
96 DType::BF16 => bf16::from_bits(bf16::ONE.to_bits() - 1).to_f32(),
97 _ => {
98 return Err(Error::UnsupportedDType {
99 op: "uniform",
100 dtype,
101 });
102 }
103 })
104}
105
106pub fn uniform(
109 low: f64,
110 high: f64,
111 shape: &[usize],
112 dtype: DType,
113 key: Option<&Array>,
114) -> Result<Array> {
115 let upper = below_one(dtype)?;
116 let lo = Array::scalar(low).astype(dtype)?;
117 let hi = Array::scalar(high).astype(dtype)?;
118 let range = subtract(&hi, &lo)?;
119 let out = divide(&bits(shape, 4, key)?, Array::scalar(u32::MAX as f32))?;
120 let out = minimum(&out, Array::scalar(upper))?.astype(dtype)?;
121 add(multiply(&range, &out)?, &lo)
122}
123
124pub fn normal(
127 shape: &[usize],
128 dtype: DType,
129 loc: Option<f64>,
130 scale: Option<f64>,
131 key: Option<&Array>,
132) -> Result<Array> {
133 if !matches!(dtype, DType::F16 | DType::BF16 | DType::F32 | DType::F64) {
134 return Err(Error::UnsupportedDType {
135 op: "normal",
136 dtype,
137 });
138 }
139 let low = f64::from(f32::from_bits((-1.0f32).to_bits() - 1));
140 let samples = uniform(low, 1.0, shape, DType::F32, key)?;
141 let mut applied = Array::scalar(2f64.sqrt()).astype(dtype)?;
142 if let Some(scale) = scale {
143 applied = multiply(&applied, Array::scalar(scale).astype(dtype)?)?;
144 }
145 let samples = multiply(&applied, erfinv(&samples)?.astype(dtype)?)?;
146 match loc {
147 Some(loc) => add(Array::scalar(loc).astype(dtype)?, &samples),
148 None => Ok(samples),
149 }
150}
151
152pub fn gumbel(shape: &[usize], dtype: DType, key: Option<&Array>) -> Result<Array> {
155 let u = uniform(0.0, 1.0, shape, dtype, key)?;
156 crate::ops::negative(&crate::ops::log(&crate::ops::negative(&crate::ops::log(
157 &u,
158 )?)?)?)
159}
160
161pub fn categorical(logits: &Array, axis: isize, key: Option<&Array>) -> Result<Array> {
177 let noise = gumbel(logits.shape(), logits.dtype(), key)?;
178 crate::ops::argmax(&add(&noise, logits)?, Some(axis), false)
179}
180
181pub fn randint(
184 low: i64,
185 high: i64,
186 shape: &[usize],
187 dtype: DType,
188 key: Option<&Array>,
189) -> Result<Array> {
190 if dtype.category() == crate::Category::Float || dtype == DType::Complex64 {
191 return Err(Error::UnsupportedDType {
192 op: "randint",
193 dtype,
194 });
195 }
196 let u = uniform(low as f64, high as f64, shape, DType::F32, key)?;
197 let u = crate::ops::maximum(&u, Array::scalar(low as f32))?;
198 let t = u.astype(DType::I64)?;
200 let up = crate::ops::less(&u, &t.astype(DType::F32)?)?;
201 subtract(&t, up.astype(DType::I64)?)?.astype(dtype)
202}
203
204pub fn bernoulli(p: f64, shape: &[usize], key: Option<&Array>) -> Result<Array> {
206 crate::ops::less(&uniform(0.0, 1.0, shape, DType::F32, key)?, p)
207}
208
209#[cfg(test)]
210mod tests {
211 use super::*;
212 use crate::kernels::threefry2x32;
213
214 #[test]
215 fn threefry_known_answer() {
216 assert_eq!(threefry2x32((0, 0), (0, 0)), (0x6b20_0159, 0x99ba_4efe));
218 }
219
220 #[test]
221 fn keys_and_layout() {
222 assert_eq!(key(1 << 32 | 7).to_vec::<u32>().unwrap(), [1, 7]);
223 let k = key(3);
225 for (n, width) in [(5, 4), (3, 2), (7, 1), (0, 4)] {
226 let b = bits(&[n], width, Some(&k)).unwrap();
227 b.eval().unwrap();
228 assert_eq!(b.size(), n);
229 }
230 assert!(bits(&[2], 3, Some(&k)).is_err());
231 assert!(bits(&[2], 4, Some(&Array::scalar(1u32))).is_err());
232 }
233
234 #[test]
235 fn sequences_are_per_thread_and_reproducible() {
236 seed(42);
237 let a = uniform(0.0, 1.0, &[4], DType::F32, None)
238 .unwrap()
239 .to_vec::<f32>()
240 .unwrap();
241 seed(42);
242 let b = uniform(0.0, 1.0, &[4], DType::F32, None)
243 .unwrap()
244 .to_vec::<f32>()
245 .unwrap();
246 assert_eq!(a, b);
247 assert!(a.iter().all(|&x| (0.0..1.0).contains(&x)));
248 }
249}