converge-analytics 3.7.2

Analytics and ML pipeline for Converge agents
Documentation
use converge_pack::gate::GateResult as Result;
use serde::{Deserialize, Serialize};

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SegmentationInput {
    pub records: Vec<Vec<f64>>,
    pub k: usize,
    #[serde(default = "default_max_iterations")]
    pub max_iterations: usize,
    pub seed: Option<u64>,
}

fn default_max_iterations() -> usize {
    100
}

impl SegmentationInput {
    pub fn validate(&self) -> Result<()> {
        if self.records.is_empty() {
            return Err(converge_pack::GateError::invalid_input(
                "At least one record required",
            ));
        }
        if self.k == 0 {
            return Err(converge_pack::GateError::invalid_input("k must be >= 1"));
        }
        if self.k > self.records.len() {
            return Err(converge_pack::GateError::invalid_input(
                "k cannot exceed number of records",
            ));
        }
        if self.max_iterations == 0 {
            return Err(converge_pack::GateError::invalid_input(
                "max_iterations must be >= 1",
            ));
        }
        let dim = self.records[0].len();
        if dim == 0 {
            return Err(converge_pack::GateError::invalid_input(
                "Records must have at least one feature",
            ));
        }
        for (i, record) in self.records.iter().enumerate() {
            if record.len() != dim {
                return Err(converge_pack::GateError::invalid_input(format!(
                    "Record {} has {} features, expected {}",
                    i,
                    record.len(),
                    dim
                )));
            }
        }
        Ok(())
    }
}

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SegmentationOutput {
    pub assignments: Vec<usize>,
    pub centroids: Vec<Vec<f64>>,
    pub iterations_used: usize,
    pub inertia: f64,
}

impl SegmentationOutput {
    pub fn summary(&self) -> String {
        format!(
            "Segmented {} records into {} clusters in {} iterations (inertia: {:.2})",
            self.assignments.len(),
            self.centroids.len(),
            self.iterations_used,
            self.inertia,
        )
    }
}