oxihuman_morph/
param_randomizer.rs1#![allow(dead_code)]
2#[allow(dead_code)]
5#[derive(Clone, Debug)]
6pub struct ParamRandomizer {
7 seed: u64,
8 min: f32,
9 max: f32,
10 count: usize,
11}
12
13#[allow(dead_code)]
14pub fn new_param_randomizer(min: f32, max: f32) -> ParamRandomizer {
15 ParamRandomizer {
16 seed: 42,
17 min,
18 max,
19 count: 0,
20 }
21}
22
23#[allow(dead_code)]
24pub fn randomize_in_range(r: &mut ParamRandomizer) -> f32 {
25 r.seed = r
26 .seed
27 .wrapping_mul(6364136223846793005)
28 .wrapping_add(1442695040888963407);
29 r.count += 1;
30 let frac = ((r.seed >> 33) as f32) / (u32::MAX as f32);
31 r.min + (r.max - r.min) * frac.clamp(0.0, 1.0)
32}
33
34#[allow(dead_code)]
43pub fn randomize_gaussian_stub(r: &mut ParamRandomizer) -> f32 {
44 let mut u1 = {
46 r.seed = r
47 .seed
48 .wrapping_mul(6364136223846793005)
49 .wrapping_add(1442695040888963407);
50 r.count += 1;
51 ((r.seed >> 33) as f32) / (u32::MAX as f32)
52 };
53 let u2 = {
54 r.seed = r
55 .seed
56 .wrapping_mul(6364136223846793005)
57 .wrapping_add(1442695040888963407);
58 r.count += 1;
59 ((r.seed >> 33) as f32) / (u32::MAX as f32)
60 };
61
62 if u1 < 1e-10 {
64 u1 = 1e-10_f32;
65 }
66
67 let z = (-2.0_f32 * u1.ln()).sqrt() * (2.0_f32 * std::f32::consts::PI * u2).cos();
69
70 let mean = (r.min + r.max) * 0.5_f32;
74 let std_dev = (r.max - r.min) / 6.0_f32;
75
76 (mean + z * std_dev).clamp(r.min, r.max)
77}
78
79#[allow(dead_code)]
80pub fn seed_randomizer(r: &mut ParamRandomizer, seed: u64) {
81 r.seed = seed;
82 r.count = 0;
83}
84
85#[allow(dead_code)]
86pub fn param_min(r: &ParamRandomizer) -> f32 {
87 r.min
88}
89
90#[allow(dead_code)]
91pub fn param_max(r: &ParamRandomizer) -> f32 {
92 r.max
93}
94
95#[allow(dead_code)]
96pub fn randomized_count(r: &ParamRandomizer) -> usize {
97 r.count
98}
99
100#[allow(dead_code)]
101pub fn randomizer_to_json(r: &ParamRandomizer) -> String {
102 format!(
103 "{{\"seed\":{},\"min\":{},\"max\":{},\"count\":{}}}",
104 r.seed, r.min, r.max, r.count
105 )
106}
107
108#[cfg(test)]
109mod tests {
110 use super::*;
111
112 #[test]
113 fn test_new_param_randomizer() {
114 let r = new_param_randomizer(0.0, 1.0);
115 assert!((param_min(&r)).abs() < 1e-6);
116 assert!((param_max(&r) - 1.0).abs() < 1e-6);
117 }
118
119 #[test]
120 fn test_randomize_in_range() {
121 let mut r = new_param_randomizer(0.0, 1.0);
122 let v = randomize_in_range(&mut r);
123 assert!((0.0..=1.0).contains(&v));
124 }
125
126 #[test]
127 fn test_randomize_gaussian_stub() {
128 let mut r = new_param_randomizer(0.0, 1.0);
129 let v = randomize_gaussian_stub(&mut r);
130 assert!((0.0..=1.0).contains(&v));
131 }
132
133 #[test]
134 fn test_seed_randomizer() {
135 let mut r = new_param_randomizer(0.0, 1.0);
136 seed_randomizer(&mut r, 123);
137 let v1 = randomize_in_range(&mut r);
138 seed_randomizer(&mut r, 123);
139 let v2 = randomize_in_range(&mut r);
140 assert!((v1 - v2).abs() < 1e-6);
141 }
142
143 #[test]
144 fn test_randomized_count() {
145 let mut r = new_param_randomizer(0.0, 1.0);
146 assert_eq!(randomized_count(&r), 0);
147 randomize_in_range(&mut r);
148 assert_eq!(randomized_count(&r), 1);
149 }
150
151 #[test]
152 fn test_randomizer_to_json() {
153 let r = new_param_randomizer(0.0, 1.0);
154 let json = randomizer_to_json(&r);
155 assert!(json.contains("\"seed\":"));
156 }
157
158 #[test]
159 fn test_range_min_max() {
160 let mut r = new_param_randomizer(5.0, 10.0);
161 for _ in 0..20 {
162 let v = randomize_in_range(&mut r);
163 assert!((5.0..=10.0).contains(&v));
164 }
165 }
166
167 #[test]
168 fn test_param_min() {
169 let r = new_param_randomizer(-1.0, 1.0);
170 assert!((param_min(&r) - (-1.0)).abs() < 1e-6);
171 }
172
173 #[test]
174 fn test_param_max() {
175 let r = new_param_randomizer(0.0, 2.0);
176 assert!((param_max(&r) - 2.0).abs() < 1e-6);
177 }
178
179 #[test]
180 fn test_deterministic() {
181 let mut r1 = new_param_randomizer(0.0, 1.0);
182 let mut r2 = new_param_randomizer(0.0, 1.0);
183 for _ in 0..10 {
184 assert!((randomize_in_range(&mut r1) - randomize_in_range(&mut r2)).abs() < 1e-6);
185 }
186 }
187
188 #[test]
189 fn randomize_gaussian_mean_centered() {
190 let min = 0.0_f32;
193 let max = 10.0_f32;
194 let expected_mean = (min + max) * 0.5;
195 let mut r = new_param_randomizer(min, max);
196 let n = 1000;
197 let sum: f32 = (0..n).map(|_| randomize_gaussian_stub(&mut r)).sum();
198 let sample_mean = sum / n as f32;
199 assert!(
200 (sample_mean - expected_mean).abs() < 0.5,
201 "sample mean {sample_mean:.4} not within 0.5 of expected {expected_mean}"
202 );
203 }
204}