use crate::core::{ConfigError, FormicaXError};
use std::time::Duration;
#[derive(Debug, Clone, PartialEq)]
pub enum PreferenceMethod {
Median,
Minimum,
Custom(f64),
}
impl Default for PreferenceMethod {
fn default() -> Self {
PreferenceMethod::Median
}
}
#[derive(Debug, Clone)]
pub struct AffinityPropagationConfig {
pub damping: f64,
pub max_iterations: usize,
pub tolerance: f64,
pub preference: PreferenceMethod,
pub parallel: bool,
pub num_threads: usize,
pub timeout: Option<Duration>,
}
impl Default for AffinityPropagationConfig {
fn default() -> Self {
Self {
damping: 0.5,
max_iterations: 200,
tolerance: 1e-6,
preference: PreferenceMethod::Median,
parallel: false,
num_threads: num_cpus::get(),
timeout: Some(Duration::from_secs(30)),
}
}
}
impl AffinityPropagationConfig {
pub fn validate(&self) -> Result<(), FormicaXError> {
if self.damping < 0.0 || self.damping >= 1.0 {
return Err(FormicaXError::Config(ConfigError::ValidationFailed {
message: "damping must be in [0, 1)".to_string(),
}));
}
if self.max_iterations == 0 {
return Err(FormicaXError::Config(ConfigError::ValidationFailed {
message: "max_iterations must be greater than 0".to_string(),
}));
}
if self.tolerance <= 0.0 {
return Err(FormicaXError::Config(ConfigError::ValidationFailed {
message: "tolerance must be positive".to_string(),
}));
}
if self.num_threads == 0 {
return Err(FormicaXError::Config(ConfigError::ValidationFailed {
message: "num_threads must be greater than 0".to_string(),
}));
}
Ok(())
}
}
#[derive(Debug, Default)]
pub struct AffinityPropagationConfigBuilder {
config: AffinityPropagationConfig,
}
impl AffinityPropagationConfigBuilder {
pub fn new() -> Self {
Self {
config: AffinityPropagationConfig::default(),
}
}
pub fn damping(mut self, damping: f64) -> Self {
self.config.damping = damping;
self
}
pub fn max_iterations(mut self, max_iterations: usize) -> Self {
self.config.max_iterations = max_iterations;
self
}
pub fn tolerance(mut self, tolerance: f64) -> Self {
self.config.tolerance = tolerance;
self
}
pub fn preference(mut self, preference: PreferenceMethod) -> Self {
self.config.preference = preference;
self
}
pub fn parallel(mut self, parallel: bool) -> Self {
self.config.parallel = parallel;
self
}
pub fn num_threads(mut self, num_threads: usize) -> Self {
self.config.num_threads = num_threads;
self
}
pub fn timeout(mut self, timeout: Duration) -> Self {
self.config.timeout = Some(timeout);
self
}
pub fn build(self) -> Result<AffinityPropagationConfig, FormicaXError> {
self.config.validate()?;
Ok(self.config)
}
}
impl AffinityPropagationConfig {
pub fn builder() -> AffinityPropagationConfigBuilder {
AffinityPropagationConfigBuilder::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_affinity_propagation_config_default() {
let config = AffinityPropagationConfig::default();
assert_eq!(config.damping, 0.5);
assert_eq!(config.max_iterations, 200);
assert_eq!(config.tolerance, 1e-6);
assert_eq!(config.preference, PreferenceMethod::Median);
assert!(!config.parallel);
}
#[test]
fn test_affinity_propagation_config_builder() {
let config = AffinityPropagationConfig::builder()
.damping(0.7)
.max_iterations(300)
.tolerance(1e-8)
.preference(PreferenceMethod::Minimum)
.parallel(true)
.num_threads(4)
.timeout(Duration::from_secs(60))
.build()
.unwrap();
assert_eq!(config.damping, 0.7);
assert_eq!(config.max_iterations, 300);
assert_eq!(config.tolerance, 1e-8);
assert_eq!(config.preference, PreferenceMethod::Minimum);
assert!(config.parallel);
assert_eq!(config.num_threads, 4);
assert_eq!(config.timeout, Some(Duration::from_secs(60)));
}
#[test]
fn test_affinity_propagation_config_validation_success() {
let config = AffinityPropagationConfig::builder()
.damping(0.5)
.max_iterations(200)
.tolerance(1e-6)
.num_threads(1)
.build()
.unwrap();
assert!(config.validate().is_ok());
}
#[test]
fn test_affinity_propagation_config_validation_invalid_damping() {
let config = AffinityPropagationConfig::builder().damping(1.5).build();
assert!(config.is_err());
}
#[test]
fn test_affinity_propagation_config_validation_zero_iterations() {
let config = AffinityPropagationConfig::builder()
.max_iterations(0)
.build();
assert!(config.is_err());
}
#[test]
fn test_affinity_propagation_config_validation_negative_tolerance() {
let config = AffinityPropagationConfig::builder().tolerance(-1.0).build();
assert!(config.is_err());
}
#[test]
fn test_affinity_propagation_config_validation_zero_threads() {
let config = AffinityPropagationConfig::builder().num_threads(0).build();
assert!(config.is_err());
}
}