runmat_runtime/builtins/stats/random/
stochastic_evolution.rs1use crate::builtins::common::random;
5use runmat_builtins::Tensor;
6
7pub fn stochastic_evolution_host(
8 tensor: &mut Tensor,
9 drift: f64,
10 scale: f64,
11 steps: u32,
12) -> Result<(), String> {
13 if tensor.data.is_empty() || steps == 0 {
14 return Ok(());
15 }
16
17 let len = tensor.data.len();
18 for _ in 0..steps {
19 let samples = random::generate_normal(len, "stochastic_evolution_host")?;
20 for (value, noise) in tensor.data.iter_mut().zip(samples.iter()) {
21 let term = drift + scale * noise;
22 *value *= term.exp();
23 }
24 }
25
26 Ok(())
27}
28
29#[cfg(test)]
30mod tests {
31 use super::*;
32 use crate::builtins::common::random;
33
34 #[test]
35 fn cpu_fallback_handles_zero_scale() {
36 let _guard = random::test_lock().lock().unwrap();
37 random::reset_rng();
38 let mut tensor = Tensor::new(vec![1.0, 2.0], vec![2, 1]).expect("tensor");
39 stochastic_evolution_host(&mut tensor, 0.1, 0.0, 3).expect("evolve");
40 let expected = (0..2)
41 .map(|i| (i as f64 + 1.0) * (0.1f64 * 3.0).exp())
42 .collect::<Vec<_>>();
43 assert_eq!(tensor.shape, vec![2, 1]);
44 for (got, exp) in tensor.data.iter().zip(expected.iter()) {
45 assert!((got - exp).abs() < 1e-12, "got {got} expected {exp}");
46 }
47 }
48}