1use crate::control::{Automaton, NoAction, Range, Time};
9
10#[derive(Debug, Clone, Copy, PartialEq)]
12pub enum RandomType {
13 Walk,
15 Logistic,
17 Henon,
19 Lorenz,
21 WhiteNoise,
23 PinkNoise,
25 BrownNoise,
27}
28
29#[derive(Debug, Clone)]
31pub struct RandomState {
32 pub value: f64,
34 pub rng_state: u64,
36 pub extra: Vec<f64>,
38 pub last_time: Time,
40}
41
42#[derive(Debug, Clone)]
44pub struct RandomAutomaton {
45 name: String,
47 rng_type: RandomType,
49 range: Range,
51 rate: f64,
53 chaos_params: (f64, f64, f64), update_rate: f64,
57}
58
59impl RandomAutomaton {
60 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 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 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 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 pub fn with_range(mut self, range: Range) -> Self {
110 self.range = range;
111 self
112 }
113
114 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 fn random_f64(&self, state: &mut u64) -> f64 {
126 self.xorshift(state) as f64 / u64::MAX as f64
127 }
128
129 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 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 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 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}