use crate::preprocess::BinDepth;
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct PairStats {
pub sum: f32,
pub count: f32,
}
#[derive(Clone, Copy, Debug, PartialEq)]
pub struct EffectContext {
pub global_mean_outcome: f32,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum KgPairActivation {
#[default]
LogFrequencyWeightedMean,
}
impl KgPairActivation {
pub fn activate(&self, stats: PairStats) -> f32 {
match self {
KgPairActivation::LogFrequencyWeightedMean => log_frequency_weighted_mean(stats),
}
}
}
fn log_frequency_weighted_mean(stats: PairStats) -> f32 {
if stats.count <= 1.0 {
0.0
} else {
(stats.sum / stats.count) * stats.count.log10()
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)]
pub enum EffectActivation {
#[default]
GlobalMeanContrast,
}
impl EffectActivation {
pub fn activate(&self, combined: f32, ctx: &EffectContext) -> f32 {
match self {
EffectActivation::GlobalMeanContrast => combined - ctx.global_mean_outcome,
}
}
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct ActivationConfig {
pub kg_pair: KgPairActivation,
pub effect: EffectActivation,
}
#[derive(Clone, Debug, Default, PartialEq, Eq)]
pub struct TransformLimits {
pub max_rows: Option<usize>,
pub max_active_columns: Option<usize>,
pub max_col_pairs: Option<usize>,
pub max_vals_map_keys: Option<usize>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct TransformConfig {
pub bin_depth: BinDepth,
pub activation: ActivationConfig,
pub verbose: bool,
pub limits: TransformLimits,
}
impl TransformConfig {
pub fn new(bin_depth: BinDepth) -> Self {
Self {
bin_depth,
activation: ActivationConfig::default(),
verbose: false,
limits: TransformLimits::default(),
}
}
pub fn with_activation(mut self, activation: ActivationConfig) -> Self {
self.activation = activation;
self
}
pub fn with_verbose(mut self, verbose: bool) -> Self {
self.verbose = verbose;
self
}
pub fn with_limits(mut self, limits: TransformLimits) -> Self {
self.limits = limits;
self
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn log_frequency_weighted_mean_cases() {
let act = KgPairActivation::LogFrequencyWeightedMean;
assert_eq!(
act.activate(PairStats {
sum: 10.0,
count: 1.0
}),
0.0
);
assert_eq!(
act.activate(PairStats {
sum: 5.0,
count: 0.5
}),
0.0
);
let v = act.activate(PairStats {
sum: 50.0,
count: 10.0,
});
assert!((v - 5.0_f32).abs() < 1e-5);
}
#[test]
fn global_mean_contrast() {
let act = EffectActivation::GlobalMeanContrast;
let ctx = EffectContext {
global_mean_outcome: 1.0,
};
assert!((act.activate(3.0, &ctx) - 2.0).abs() < 1e-6);
}
}