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
40fn next_key() -> Result<Array> {
42 let current = SEQUENCE.with_borrow_mut(|k| k.get_or_insert_with(|| key(time_seed())).clone());
43 let (keep, out) = split_pair(¤t)?;
44 SEQUENCE.set(Some(keep));
45 Ok(out)
46}
47
48fn key_or_next(key: Option<&Array>) -> Result<Array> {
49 match key {
50 Some(k) => Ok(k.clone()),
51 None => next_key(),
52 }
53}
54
55pub fn bits(shape: &[usize], width: usize, key: Option<&Array>) -> Result<Array> {
57 let key = key_or_next(key)?;
58 if key.dtype() != DType::U32 || key.shape() != [2] {
59 return Err(Error::InvalidArgument(format!(
60 "bits: expected a uint32 key of shape [2], got {} {:?}",
61 key.dtype(),
62 key.shape()
63 )));
64 }
65 let width = u8::try_from(width)
66 .map_err(|_| Error::InvalidArgument(format!("bits: width {width} not in {{1, 2, 4}}")))?;
67 let primitive = Primitive::RandomBits {
68 shape: shape.to_vec(),
69 width,
70 };
71 Ok(Array::from_op(primitive, vec![key])?.swap_remove(0))
72}
73
74pub fn split(key: &Array, num: usize) -> Result<Array> {
76 bits(&[num, 2], 4, Some(key))
77}
78
79pub fn split_pair(key: &Array) -> Result<(Array, Array)> {
81 let both = split(key, 2)?;
82 let row = |i: isize| {
83 let args = [SliceArg::new(Some(i), Some(i + 1), 1), SliceArg::full()];
84 slice(&both, &args)?.reshape(&[2])
85 };
86 Ok((row(0)?, row(1)?))
87}
88
89fn below_one(dtype: DType) -> Result<f32> {
91 Ok(match dtype {
92 DType::F32 => f32::from_bits(1.0f32.to_bits() - 1),
93 DType::F16 => f16::from_bits(f16::ONE.to_bits() - 1).to_f32(),
94 DType::BF16 => bf16::from_bits(bf16::ONE.to_bits() - 1).to_f32(),
95 _ => {
96 return Err(Error::UnsupportedDType {
97 op: "uniform",
98 dtype,
99 });
100 }
101 })
102}
103
104pub fn uniform(
107 low: f64,
108 high: f64,
109 shape: &[usize],
110 dtype: DType,
111 key: Option<&Array>,
112) -> Result<Array> {
113 let upper = below_one(dtype)?;
114 let lo = Array::scalar(low).astype(dtype)?;
115 let hi = Array::scalar(high).astype(dtype)?;
116 let range = subtract(&hi, &lo)?;
117 let out = divide(&bits(shape, 4, key)?, Array::scalar(u32::MAX as f32))?;
118 let out = minimum(&out, Array::scalar(upper))?.astype(dtype)?;
119 add(multiply(&range, &out)?, &lo)
120}
121
122pub fn normal(
125 shape: &[usize],
126 dtype: DType,
127 loc: Option<f64>,
128 scale: Option<f64>,
129 key: Option<&Array>,
130) -> Result<Array> {
131 if !matches!(dtype, DType::F16 | DType::BF16 | DType::F32 | DType::F64) {
132 return Err(Error::UnsupportedDType {
133 op: "normal",
134 dtype,
135 });
136 }
137 let low = f64::from(f32::from_bits((-1.0f32).to_bits() - 1));
138 let samples = uniform(low, 1.0, shape, DType::F32, key)?;
139 let mut applied = Array::scalar(2f64.sqrt()).astype(dtype)?;
140 if let Some(scale) = scale {
141 applied = multiply(&applied, Array::scalar(scale).astype(dtype)?)?;
142 }
143 let samples = multiply(&applied, erfinv(&samples)?.astype(dtype)?)?;
144 match loc {
145 Some(loc) => add(Array::scalar(loc).astype(dtype)?, &samples),
146 None => Ok(samples),
147 }
148}
149
150pub fn gumbel(shape: &[usize], dtype: DType, key: Option<&Array>) -> Result<Array> {
153 let u = uniform(0.0, 1.0, shape, dtype, key)?;
154 crate::ops::negative(&crate::ops::log(&crate::ops::negative(&crate::ops::log(
155 &u,
156 )?)?)?)
157}
158
159pub fn categorical(logits: &Array, axis: isize, key: Option<&Array>) -> Result<Array> {
175 let noise = gumbel(logits.shape(), logits.dtype(), key)?;
176 crate::ops::argmax(&add(&noise, logits)?, Some(axis), false)
177}
178
179pub fn randint(
182 low: i64,
183 high: i64,
184 shape: &[usize],
185 dtype: DType,
186 key: Option<&Array>,
187) -> Result<Array> {
188 if dtype.category() == crate::Category::Float || dtype == DType::Complex64 {
189 return Err(Error::UnsupportedDType {
190 op: "randint",
191 dtype,
192 });
193 }
194 let u = uniform(low as f64, high as f64, shape, DType::F32, key)?;
195 let u = crate::ops::maximum(&u, Array::scalar(low as f32))?;
196 let t = u.astype(DType::I64)?;
198 let up = crate::ops::less(&u, &t.astype(DType::F32)?)?;
199 subtract(&t, up.astype(DType::I64)?)?.astype(dtype)
200}
201
202pub fn bernoulli(p: f64, shape: &[usize], key: Option<&Array>) -> Result<Array> {
204 crate::ops::less(&uniform(0.0, 1.0, shape, DType::F32, key)?, p)
205}
206
207#[cfg(test)]
208mod tests {
209 use super::*;
210 use crate::kernels::threefry2x32;
211
212 #[test]
213 fn threefry_known_answer() {
214 assert_eq!(threefry2x32((0, 0), (0, 0)), (0x6b20_0159, 0x99ba_4efe));
216 }
217
218 #[test]
219 fn keys_and_layout() {
220 assert_eq!(key(1 << 32 | 7).to_vec::<u32>().unwrap(), [1, 7]);
221 let k = key(3);
223 for (n, width) in [(5, 4), (3, 2), (7, 1), (0, 4)] {
224 let b = bits(&[n], width, Some(&k)).unwrap();
225 b.eval().unwrap();
226 assert_eq!(b.size(), n);
227 }
228 assert!(bits(&[2], 3, Some(&k)).is_err());
229 assert!(bits(&[2], 4, Some(&Array::scalar(1u32))).is_err());
230 }
231
232 #[test]
233 fn sequences_are_per_thread_and_reproducible() {
234 seed(42);
235 let a = uniform(0.0, 1.0, &[4], DType::F32, None)
236 .unwrap()
237 .to_vec::<f32>()
238 .unwrap();
239 seed(42);
240 let b = uniform(0.0, 1.0, &[4], DType::F32, None)
241 .unwrap()
242 .to_vec::<f32>()
243 .unwrap();
244 assert_eq!(a, b);
245 assert!(a.iter().all(|&x| (0.0..1.0).contains(&x)));
246 }
247}