use crate::metric_names;
use crate::{Expr, stats::metric_fields};
const KP: f32 = 0.05_f32;
const KI: f32 = 0.005_f32;
const KD: f32 = 0.02_f32;
pub fn species_error_signal(count: usize) -> Expr {
Expr::select(metric_names::SPECIES_COUNT).error(count as f32)
}
pub fn species_target_control(target: usize, base_val: f32) -> Expr {
let target_f32 = target as f32;
let raw_error = species_error_signal(target);
let proportional = Expr::select(metric_names::SPECIES_COUNT)
.rolling(3)
.mean()
.error(target_f32)
* KP;
let integral = raw_error.clone().rolling(20).sum() * KI;
let derivative = raw_error.rolling(5).slope() * KD;
Expr::when(Expr::select(metric_names::INDEX).lt(2_i32))
.then(base_val)
.otherwise(
Expr::select(metric_names::SPECIES_THRESHOLD) + proportional + integral + derivative,
)
.clamp(0.0_f32, target_f32 * 2.5_f32)
.alias(metric_names::SPECIES_THRESHOLD)
}
pub fn score_trend_signal(window: usize) -> Expr {
Expr::select(metric_names::BEST_SCORES)
.rolling(window)
.slope()
.alias(format!("{}.[{}]", metric_names::SCORES_TREND, window))
}
pub fn score_cv_signal(window: usize) -> Expr {
Expr::select(metric_names::BEST_SCORES)
.rolling(window)
.stddev()
.div(
Expr::select(metric_names::BEST_SCORES)
.rolling(window)
.mean(),
)
}
pub fn genome_size_throttle(base_rate: impl Into<Expr>, target_size: usize) -> Expr {
let pressure = Expr::select(metric_names::GENOME_SIZE)
.rolling(10)
.mean()
.div(target_size as f32)
.clamp(1.0_f32, 5.0_f32);
base_rate.into().div(pressure)
}
pub fn diversity_signal(window: usize, min: f32, max: f32) -> Expr {
let diversity = Expr::select(metric_names::PCT_DIVERSITY)
.rolling(window)
.mean();
(Expr::lit(1.0_f32) - diversity)
.mul(max - min)
.add(min)
.clamp(min, max)
.alias(format!("{}.[{}]", metric_names::PCT_DIVERSITY, window))
}
pub fn stagnation_expr(window: usize, epsilon: f32) -> Expr {
Expr::select(metric_names::BEST_SCORES)
.rolling(window)
.slope()
.abs()
.lt(epsilon)
}
pub fn bloat_pressure_signal(base_rate: impl Into<Expr>, corr_floor: f32) -> Expr {
let base_rate = base_rate.into();
let growing = Expr::select(metric_names::GENOME_SIZE)
.attr(metric_fields::MEAN)
.rolling(10)
.slope()
.gt(0.0_f32);
let weak_payoff = Expr::select(metric_names::SIZE_SCORE_CORR)
.rolling(10)
.mean()
.abs()
.lt(corr_floor);
Expr::warmup(1)
.then(
Expr::when(growing.and(weak_payoff))
.then(base_rate.clone() * 0.5_f32)
.otherwise(base_rate.clone()),
)
.otherwise(base_rate)
}