Skip to main content

rill_patchbay/automaton/
random.rs

1//! # Случайные процессы
2//!
3//! Автоматы для генерации случайных и псевдослучайных последовательностей:
4//! - Random Walk (случайное блуждание)
5//! - Chaos (детерминированный хаос)
6//! - Noise (белый, розовый, коричневый шум)
7
8use crate::control::{Automaton, NoAction, Range, Time};
9
10/// Тип случайного процесса
11#[derive(Debug, Clone, Copy, PartialEq)]
12pub enum RandomType {
13    /// Случайное блуждание
14    Walk,
15    /// Логистическое отображение (хаос)
16    Logistic,
17    /// Отображение Эно
18    Henon,
19    /// Отображение Лоренца
20    Lorenz,
21    /// Белый шум
22    WhiteNoise,
23    /// Розовый шум (1/f)
24    PinkNoise,
25    /// Коричневый шум (1/f²)
26    BrownNoise,
27}
28
29/// Состояние случайного процесса
30#[derive(Debug, Clone)]
31pub struct RandomState {
32    /// Текущее значение
33    pub value: f64,
34    /// Внутреннее состояние RNG
35    pub rng_state: u64,
36    /// Дополнительные состояния для сложных процессов
37    pub extra: Vec<f64>,
38    /// Время последнего обновления
39    pub last_time: Time,
40}
41
42/// Автомат случайного процесса
43#[derive(Debug, Clone)]
44pub struct RandomAutomaton {
45    /// Имя автомата
46    name: String,
47    /// Тип случайного процесса
48    rng_type: RandomType,
49    /// Диапазон выходных значений
50    range: Range,
51    /// Скорость изменения (для Walk)
52    rate: f64,
53    /// Параметры хаоса
54    chaos_params: (f64, f64, f64), // r, a, b и т.д.
55    /// Частота обновления (для шума)
56    update_rate: f64,
57}
58
59impl RandomAutomaton {
60    /// Создать новый Random Walk
61    pub fn walk(name: &str, rate: f64) -> Self {
62        Self {
63            name: name.to_string(),
64            rng_type: RandomType::Walk,
65            range: Range::bipolar(),
66            rate: rate.max(0.0),
67            chaos_params: (0.0, 0.0, 0.0),
68            update_rate: 0.0,
69        }
70    }
71
72    /// Создать логистическое отображение (хаос)
73    pub fn logistic(name: &str, r: f64) -> Self {
74        Self {
75            name: name.to_string(),
76            rng_type: RandomType::Logistic,
77            range: Range::unipolar(),
78            rate: 0.0,
79            chaos_params: (r.clamp(3.0, 4.0), 0.0, 0.0),
80            update_rate: 0.0,
81        }
82    }
83
84    /// Создать отображение Эно
85    pub fn henon(name: &str, a: f64, b: f64) -> Self {
86        Self {
87            name: name.to_string(),
88            rng_type: RandomType::Henon,
89            range: Range::bipolar(),
90            rate: 0.0,
91            chaos_params: (a, b, 0.0),
92            update_rate: 0.0,
93        }
94    }
95
96    /// Создать генератор белого шума
97    pub fn white_noise(name: &str, update_rate: f64) -> Self {
98        Self {
99            name: name.to_string(),
100            rng_type: RandomType::WhiteNoise,
101            range: Range::bipolar(),
102            rate: 0.0,
103            chaos_params: (0.0, 0.0, 0.0),
104            update_rate: update_rate.max(1.0),
105        }
106    }
107
108    /// Установить диапазон
109    pub fn with_range(mut self, range: Range) -> Self {
110        self.range = range;
111        self
112    }
113
114    /// Xorshift RNG
115    fn xorshift(&self, state: &mut u64) -> u64 {
116        let mut x = *state;
117        x ^= x << 13;
118        x ^= x >> 7;
119        x ^= x << 17;
120        *state = x;
121        x
122    }
123
124    /// Случайное число в диапазоне [0, 1)
125    fn random_f64(&self, state: &mut u64) -> f64 {
126        self.xorshift(state) as f64 / u64::MAX as f64
127    }
128
129    /// Обновить Random Walk
130    fn update_walk(&self, state: &mut RandomState, dt: Time) {
131        let step = (self.random_f64(&mut state.rng_state) - 0.5) * 2.0 * self.rate * dt;
132        state.value = (state.value + step).clamp(-1.0, 1.0);
133    }
134
135    /// Обновить логистическое отображение
136    fn update_logistic(&self, state: &mut RandomState, _dt: Time) {
137        let r = self.chaos_params.0;
138        state.value = r * state.value * (1.0 - state.value);
139    }
140
141    /// Обновить отображение Эно
142    fn update_henon(&self, state: &mut RandomState, _dt: Time) {
143        let a = self.chaos_params.0;
144        let b = self.chaos_params.1;
145
146        if state.extra.is_empty() {
147            state.extra.push(0.0);
148        }
149
150        let x = state.value;
151        let y = state.extra[0];
152
153        state.value = 1.0 - a * x * x + y;
154        state.extra[0] = b * x;
155    }
156
157    /// Обновить белый шум
158    fn update_white_noise(&self, state: &mut RandomState, dt: Time) {
159        state.last_time += dt;
160        if state.last_time >= 1.0 / self.update_rate {
161            state.value = self.random_f64(&mut state.rng_state) * 2.0 - 1.0;
162            state.last_time = 0.0;
163        }
164    }
165}
166
167impl Automaton for RandomAutomaton {
168    type State = RandomState;
169    type Action = NoAction;
170
171    fn step(
172        &self,
173        time: Time,
174        _action: &Self::Action,
175        state: &Self::State,
176    ) -> (Self::State, Option<f64>) {
177        let mut new_state = state.clone();
178        let dt = time - state.last_time;
179
180        match self.rng_type {
181            RandomType::Walk => self.update_walk(&mut new_state, dt),
182            RandomType::Logistic => self.update_logistic(&mut new_state, dt),
183            RandomType::Henon => self.update_henon(&mut new_state, dt),
184            RandomType::WhiteNoise => self.update_white_noise(&mut new_state, dt),
185            _ => {}
186        }
187
188        new_state.last_time = time;
189        let value = self.range.clamp(new_state.value);
190
191        (new_state, Some(value))
192    }
193
194    fn initial_state(&self) -> Self::State {
195        let rng = 123456789;
196        let initial_value = match self.rng_type {
197            RandomType::Logistic => 0.5,
198            RandomType::Henon => 0.0,
199            _ => 0.0,
200        };
201
202        RandomState {
203            value: initial_value,
204            rng_state: rng,
205            extra: Vec::new(),
206            last_time: 0.0,
207        }
208    }
209
210    fn name(&self) -> &str {
211        &self.name
212    }
213
214    fn extract_value(&self, state: &Self::State) -> f64 {
215        self.range.clamp(state.value)
216    }
217}
218
219#[cfg(test)]
220mod tests {
221    use super::*;
222
223    #[test]
224    fn test_random_walk() {
225        let walk = RandomAutomaton::walk("Walk", 0.1);
226        let state = walk.initial_state();
227
228        let (_state, value) = walk.step(0.01, &NoAction, &state);
229        assert!(value.unwrap() >= -1.0 && value.unwrap() <= 1.0);
230    }
231
232    #[test]
233    fn test_logistic() {
234        let logistic = RandomAutomaton::logistic("Logistic", 3.8);
235        let state = logistic.initial_state();
236
237        let (_state, value) = logistic.step(0.0, &NoAction, &state);
238        assert!(value.unwrap() >= 0.0 && value.unwrap() <= 1.0);
239    }
240}