radiate_core/domain/
macros.rs

1#[macro_export]
2macro_rules! impl_integer {
3    ($($t:ty),*) => {
4        $(
5            impl Integer<$t> for $t {
6                const MIN: $t = <$t>::MIN;
7                const MAX: $t = <$t>::MAX;
8
9                fn from_i32(value: i32) -> $t {
10                    value as $t
11                }
12            }
13        )*
14    };
15}
16
17#[macro_export]
18macro_rules! alters {
19    ($($struct_instance:expr),* $(,)?) => {
20        {
21            let mut vec: Vec<Box<dyn Alter<_>>> = Vec::new();
22            $(
23                vec.push(Box::new($struct_instance.alterer()));
24            )*
25            vec
26        }
27    };
28}
29
30// #[macro_export]
31// macro_rules! alterers {
32//     ($($name:ident $( ( $($args:expr),* ) )?),* $(,)?) => {
33//         vec![
34//             $(Box::new($name $( ( $($args),* ) )? .alterer())),*
35//         ]
36//     };
37// }
38
39// .alter(alterers![
40//     ArithmeticMutator(0.01),
41//     MeanCrossover(0.5),
42//     UniformMutator, // uses default arguments, if defined
43// ])
44
45#[macro_export]
46macro_rules! bench {
47    ($name:literal, $operation:expr) => {
48        let timer = std::time::Instant::now();
49        let result = $operation;
50        let elapsed = timer.elapsed();
51        println!("{:?} took {:?}", $name, elapsed);
52        result
53    };
54}
55
56#[macro_export]
57macro_rules! print_metrics {
58    ($metric_set:expr, [$($filter:expr),* $(,)?]) => {{
59        use std::collections::HashSet;
60        let filter_set: HashSet<&str> = vec![$($filter),*].into_iter().collect();
61
62        println!("=================================================== Metrics Summary ====================================================");
63
64        // Display in order: Operations, Values, Distributions, Times
65        for metric_type in ["Operations", "Value", "Distribution", "Time"] {
66            for (name, metric) in $metric_set.iter() {
67                if !filter_set.contains(name) {
68                    continue;
69                }
70                match (metric_type, metric) {
71                    ("Operations", Metric::Operations(_, _, _)) => println!("{:?}", metric),
72                    ("Value", Metric::Value(_, _)) => println!("{:?}", metric),
73                    ("Distribution", Metric::Distribution(_, _)) => println!("{:?}", metric),
74                    ("Time", Metric::Time(_, _)) => println!("{:?}", metric),
75                    _ => {},
76                }
77            }
78        }
79        println!("========================================================================================================================");
80    }};
81    ($metric_set:expr) => {{
82        use std::time::Duration;
83
84        println!("=================================================== Metrics Summary ====================================================");
85
86        // Operations first
87        for (name, metric) in $metric_set.iter().filter(|(_, m)| matches!(m, Metric::Operations(_, _, _))) {
88            if let Metric::Operations(_, stat, time_stat) = metric {
89                println!(
90                    "{:<20} | Mean: {:>8.3}, Min: {:>8.3}, Max: {:>8.3}, N: {:>3} | Avg Time: {:>9.3?}, Total Time: {:>9.3?}",
91                    name,
92                    stat.mean(),
93                    stat.min(),
94                    stat.max(),
95                    stat.count(),
96                    time_stat.mean(),
97                    time_stat.sum(),
98                );
99            }
100        }
101
102        // Values next
103        for (name, metric) in $metric_set.iter().filter(|(_, m)| matches!(m, Metric::Value(_, _, _))) {
104            if let Metric::Value(_, stat, dist) = metric {
105                println!(
106                    "{:<20} | Mean: {:>8.3}, Min: {:>8.3}, Max: {:>8.3}, N: {:>3} | Dist. Mean: {:>8.3}, Dist. StdDev: {:>8.3}, Dist. Min: {:>8.3}, Dist. Max: {:>8.3}",
107                    name,
108                    stat.mean(),
109                    stat.min(),
110                    stat.max(),
111                    stat.count(),
112                    dist.mean(),
113                    dist.standard_deviation(),
114                    dist.min(),
115                    dist.max(),
116
117                );
118            }
119        }
120
121        // Distributions next
122        for (name, metric) in $metric_set.iter().filter(|(_, m)| matches!(m, Metric::Distribution(_, _))) {
123            if let Metric::Distribution(_, dist) = metric {
124                println!(
125                    "{:<20} | Mean: {:>8.3}, StdDev: {:>8.3}, Min: {:>8.3}, Max: {:>8.3}, N: {:>3}",
126                    name,
127                    dist.mean(),
128                    dist.standard_deviation(),
129                    dist.min(),
130                    dist.max(),
131                    dist.count(),
132                );
133            }
134        }
135
136        // Times last
137        for (name, metric) in $metric_set.iter().filter(|(_, m)| matches!(m, Metric::Time(_, _))) {
138            if let Metric::Time(_, stat) = metric {
139                println!(
140                    "{:<20} | Avg Time: {:>9.3?}, Min Time: {:>9.3?}, Max Time: {:>9.3?}, N: {:>3} | Total Time: {:>9.3?}",
141                    name,
142                    stat.mean(),
143                    stat.min(),
144                    stat.max(),
145                    stat.count(),
146                    stat.sum(),
147                );
148            }
149        }
150
151        println!("========================================================================================================================");
152    }};
153}
154
155#[macro_export]
156macro_rules! metricset_to_string {
157    ($metric_set:expr) => {{
158        use std::collections::HashSet;
159        let mut result = String::new();
160
161        for (name, metric) in $metric_set.iter() {
162            result.push_str(&format!("{:?}\n", metric));
163        }
164        result
165    }};
166}
167
168#[macro_export]
169macro_rules! log_ctx {
170    ($ctx:expr) => {{
171        println!(
172            "[ Iteration {:<4} ] Score: {:>8.4}, Elapsed: {:.2?}",
173            $ctx.index(),
174            $ctx.score().as_f32(),
175            $ctx.time()
176        );
177    }};
178}
179
180#[macro_export]
181macro_rules! metric {
182    ($name:expr, $val:expr, $time:expr) => {{ Metric::new_operations($name, $val, $time) }};
183    ($name:expr, $val:expr) => {{
184        let mut metric = Metric::new_value($name);
185        metric.add_value($val);
186        metric
187    }};
188    ($name:expr, $dist:expr) => {{
189        let mut metric = Metric::new_distribution($name)
190        metric.add_distribution($dist);
191        metric
192    }};
193    ($name:expr, $time:expr) => {{
194        let mut metric = Metric::new_time($name);
195        metric.add_time($time);
196        metric
197    }};
198}
199
200#[macro_export]
201macro_rules! histogram {
202    ($title:expr, $data:expr) => {{
203        let max = $data.iter().cloned().fold(f32::MIN, f32::max);
204        let min = $data.iter().cloned().fold(f32::MAX, f32::min);
205        let bins = 10;
206        let step = (max - min) / bins as f32;
207        for i in 0..bins {
208            let lower = min + i as f32 * step;
209            let upper = lower + step;
210            let count = $data.iter().filter(|&&x| x >= lower && x < upper).count();
211            println!("  {:6.2} - {:6.2}: {}", lower, upper, "█".repeat(count));
212        }
213    }};
214}
215
216#[macro_export]
217macro_rules! dbg_ctx {
218    ($val:expr $(,)?) => {{
219        let tmp = &$val;
220        println!("[{}:{}] {} = {:?}", file!(), line!(), stringify!($val), tmp);
221        tmp
222    }};
223}
224
225#[macro_export]
226macro_rules! build_engine {
227    (
228        codec: $codec:expr,
229        fitness: $fitness_fn:expr,
230        settings: { $( $setting:ident $( : $value:expr )? ),* $(,)? }
231    ) => {{
232        let builder = GeneticEngine::builder().codec($codec).fitness_fn($fitness_fn);
233        $(
234            #[allow(unused_mut)]
235            let builder = crate::build_engine!(@apply_setting builder, $setting $(, $value)?);
236        )*
237        builder.build()
238    }};
239
240    // helper to apply each setting appropriately
241    (@apply_setting $builder:ident, $method:ident, $value:expr) => {
242        $builder.$method($value)
243    };
244    (@apply_setting $builder:ident, $method:ident) => {
245        $builder.$method()
246    };
247}
248
249#[macro_export]
250macro_rules! engine {
251    ($codec:expr, $fitness:expr) => {
252        GeneticEngine::builder().codec($codec).fitness_fn($fitness).build()
253    };
254    ($codec:expr, $fitness:expr, $($extra:tt)+) => {
255        GeneticEngine::builder().codec($codec).fitness_fn($fitness).$($extra)+.build()
256    };
257}
258
259#[macro_export]
260macro_rules! experiment {
261    (
262        repeat: $reps:expr,
263        $codec:expr,
264        $fitness:expr,
265        [$( $setting:ident ( $($value:expr),* ) ),* $(,)?],
266        $condition:expr
267    ) => {
268        (0..$reps)
269            .map(|_| {
270                let engine = GeneticEngine::builder()
271                    .codec($codec)
272                    .fitness_fn($fitness)
273                    $( .$setting($($value),*) )*
274                    .build();
275                engine.run($condition)
276            })
277            .collect::<Vec<_>>()
278    };
279}
280
281// let results = experiment!(
282//     repeat: 10,
283//     FloatCodec::vector(5, -10.0..10.0),
284//     |geno: Vec<f32>| geno.iter().sum::<f32>(),
285//     [
286//         minimizing(),
287//         population_size(200),
288//         num_threads(4),
289//         alter(alters!(SwapMutator::new(0.05), UniformCrossover::new(0.5)))
290//     ],
291//     |ctx| ctx.score().as_f32() < 0.01
292// );
293
294// MACRO IDEAS
295
296// #[macro_export]
297// macro_rules! genetic_test {
298//     (
299//         name: $name:ident,
300//         codec: $codec:expr,
301//         fitness: $fitness_fn:expr,
302//         settings: { $( $setting:ident $( : $value:expr )? ),* $(,)? },
303//         stopping_criteria: |$ctx:ident| $criteria:expr,
304//         assert: |$result:ident| $assertion:block
305//     ) => {
306//         #[test]
307//         fn $name() {
308//             let engine = crate::build_engine!(
309//                 codec: $codec,
310//                 fitness: $fitness_fn,
311//                 settings: { $($setting $( : $value )?),* }
312//             );
313
314//             let $result = engine.run(|$ctx| $criteria);
315
316//             $assertion
317//         }
318//     };
319// }
320
321// genetic_test!(
322//     name: evolve_zero_vector,
323//     codec: FloatCodec::vector(5, -10.0..10.0),
324//     fitness: |geno| geno.iter().map(|x| x * x).sum::<f32>(),
325//     settings: {
326//         minimizing,
327//         population_size: 50,
328//         num_threads: 4,
329//     },
330//     stopping_criteria: |ctx| {
331//         // Stop when the score is close to zero
332//         ctx.score().as_f32() < 0.01
333//     },
334//     assert: |result| {
335//         assert!(result.score().as_f32() < 0.1);
336//     }
337// );