radiate_core/stats/
expr.rs1use crate::metric_names;
2use crate::{Expr, stats::metric_fields};
3
4const KP: f32 = 0.05_f32;
5const KI: f32 = 0.005_f32;
6const KD: f32 = 0.02_f32;
7
8pub fn species_error_signal(count: usize) -> Expr {
9 Expr::select(metric_names::SPECIES_COUNT).error(count as f32)
10}
11
12pub fn species_target_control(target: usize, base_val: f32) -> Expr {
13 let target_f32 = target as f32;
14 let raw_error = species_error_signal(target);
15
16 let proportional = Expr::select(metric_names::SPECIES_COUNT)
18 .rolling(3)
19 .mean()
20 .error(target_f32)
21 * KP;
22
23 let integral = raw_error.clone().rolling(20).sum() * KI;
26 let derivative = raw_error.rolling(5).slope() * KD;
27
28 Expr::when(Expr::select(metric_names::INDEX).lt(2_i32))
29 .then(base_val)
30 .otherwise(
31 Expr::select(metric_names::SPECIES_THRESHOLD) + proportional + integral + derivative,
32 )
33 .clamp(0.0_f32, target_f32 * 2.5_f32)
34 .alias(metric_names::SPECIES_THRESHOLD)
35}
36
37pub fn score_trend_signal(window: usize) -> Expr {
39 Expr::select(metric_names::BEST_SCORES)
40 .rolling(window)
41 .slope()
42 .alias(format!("{}.[{}]", metric_names::SCORES_TREND, window))
43}
44
45pub fn score_cv_signal(window: usize) -> Expr {
47 Expr::select(metric_names::BEST_SCORES)
48 .rolling(window)
49 .stddev()
50 .div(
51 Expr::select(metric_names::BEST_SCORES)
52 .rolling(window)
53 .mean(),
54 )
55}
56
57pub fn genome_size_throttle(base_rate: impl Into<Expr>, target_size: usize) -> Expr {
59 let pressure = Expr::select(metric_names::GENOME_SIZE)
60 .rolling(10)
61 .mean()
62 .div(target_size as f32)
63 .clamp(1.0_f32, 5.0_f32);
64 base_rate.into().div(pressure)
65}
66
67pub fn diversity_signal(window: usize, min: f32, max: f32) -> Expr {
69 let diversity = Expr::select(metric_names::PCT_DIVERSITY)
70 .rolling(window)
71 .mean();
72 (Expr::lit(1.0_f32) - diversity)
73 .mul(max - min)
74 .add(min)
75 .clamp(min, max)
76 .alias(format!("{}.[{}]", metric_names::PCT_DIVERSITY, window))
77}
78
79pub fn stagnation_expr(window: usize, epsilon: f32) -> Expr {
81 Expr::select(metric_names::BEST_SCORES)
82 .rolling(window)
83 .slope()
84 .abs()
85 .lt(epsilon)
86}
87
88pub fn bloat_pressure_signal(base_rate: impl Into<Expr>, corr_floor: f32) -> Expr {
92 let base_rate = base_rate.into();
93 let growing = Expr::select(metric_names::GENOME_SIZE)
94 .attr(metric_fields::MEAN)
95 .rolling(10)
96 .slope()
97 .gt(0.0_f32);
98
99 let weak_payoff = Expr::select(metric_names::SIZE_SCORE_CORR)
100 .rolling(10)
101 .mean()
102 .abs()
103 .lt(corr_floor);
104
105 Expr::warmup(1)
106 .then(
107 Expr::when(growing.and(weak_payoff))
108 .then(base_rate.clone() * 0.5_f32)
109 .otherwise(base_rate.clone()),
110 )
111 .otherwise(base_rate)
112}