use crate::config::limits::PPR;
use crate::config::mode::mode as mode_config;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ScoringMode {
Ppr,
Ego,
Bm25,
Rrf,
Pit,
}
impl ScoringMode {
pub fn from_str(s: &str) -> Result<Self, String> {
match s.to_lowercase().as_str() {
"ppr" => Ok(Self::Ppr),
"ego" => Ok(Self::Ego),
"bm25" => Ok(Self::Bm25),
"rrf" => Ok(Self::Rrf),
"pit" => Ok(Self::Pit),
other => Err(format!(
"unknown scoring_mode '{other}': expected one of {}",
SCORING_MODE_NAMES.join("|")
)),
}
}
}
pub const SCORING_MODE_NAMES: &[&str] = &["ppr", "ego", "bm25", "rrf", "pit"];
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum DiscoveryKind {
Default,
Ensemble,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ScoringKind {
Ppr,
Ego,
Bm25,
Rrf,
Pit,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ObjectiveMode {
Submodular,
BoltzmannModular,
}
impl ObjectiveMode {
pub fn from_str(s: &str) -> Self {
match s.to_lowercase().as_str() {
"boltzmann" | "boltzmann_modular" | "modular_boltzmann" => Self::BoltzmannModular,
_ => Self::Submodular,
}
}
}
#[derive(Debug, Clone)]
pub struct PipelineConfig {
pub discovery: DiscoveryKind,
pub scoring: ScoringKind,
pub objective: ObjectiveMode,
pub bm25_top_k: usize,
pub ego_depth: usize,
pub ppr_alpha: f64,
}
impl PipelineConfig {
pub fn from_mode(mode: ScoringMode) -> Self {
let m = mode_config();
match mode {
ScoringMode::Ppr => Self {
discovery: DiscoveryKind::Ensemble,
scoring: ScoringKind::Ppr,
bm25_top_k: m.bm25_top_k_primary,
ego_depth: m.ego_depth_default,
ppr_alpha: PPR.alpha,
objective: ObjectiveMode::Submodular,
},
ScoringMode::Ego => Self {
discovery: DiscoveryKind::Ensemble,
scoring: ScoringKind::Ego,
bm25_top_k: m.bm25_top_k_primary,
ego_depth: m.ego_depth_extended,
ppr_alpha: PPR.alpha,
objective: ObjectiveMode::Submodular,
},
ScoringMode::Bm25 => Self {
discovery: DiscoveryKind::Ensemble,
scoring: ScoringKind::Bm25,
bm25_top_k: m.bm25_top_k_off,
ego_depth: m.ego_depth_default,
ppr_alpha: PPR.alpha,
objective: ObjectiveMode::Submodular,
},
ScoringMode::Rrf => Self {
discovery: DiscoveryKind::Ensemble,
scoring: ScoringKind::Rrf,
bm25_top_k: m.bm25_top_k_primary,
ego_depth: m.ego_depth_extended,
ppr_alpha: PPR.alpha,
objective: ObjectiveMode::Submodular,
},
ScoringMode::Pit => Self {
discovery: DiscoveryKind::Ensemble,
scoring: ScoringKind::Pit,
bm25_top_k: m.bm25_top_k_primary,
ego_depth: m.ego_depth_extended,
ppr_alpha: PPR.alpha,
objective: ObjectiveMode::Submodular,
},
}
}
}
#[cfg(test)]
mod scoring_mode_name_tests {
use super::{SCORING_MODE_NAMES, ScoringMode};
#[test]
fn every_advertised_name_parses() {
for name in SCORING_MODE_NAMES {
assert!(
ScoringMode::from_str(name).is_ok(),
"advertised scoring mode does not parse: {name}"
);
}
}
#[test]
fn every_parsable_mode_is_advertised() {
for candidate in ["ppr", "ego", "bm25", "rrf", "pit"] {
if ScoringMode::from_str(candidate).is_ok() {
assert!(
SCORING_MODE_NAMES.contains(&candidate),
"{candidate} parses but is not advertised to the CLIs"
);
}
}
}
}