gspx 0.1.2

Sparse graph signal processing and spectral graph wavelets in Rust
Documentation
use std::collections::HashMap;

use ndarray::{Array1, Array2};

#[derive(Debug, Clone)]
/// Table of extracted modal quantities.
pub struct ModeTable {
    /// Modal frequency in Hz.
    pub frequency: Array1<f64>,
    /// Estimated damping ratio (clamped to `[0, 1]`).
    pub damping: Array1<f64>,
    /// Modal wavelength proxy derived from graph scale.
    pub wavelength: Array1<f64>,
    /// Peak spectrum magnitude.
    pub magnitude: Array1<f64>,
}

impl ModeTable {
    /// Returns an empty table.
    pub fn empty() -> Self {
        Self {
            frequency: Array1::zeros(0),
            damping: Array1::zeros(0),
            wavelength: Array1::zeros(0),
            magnitude: Array1::zeros(0),
        }
    }

    /// Returns the number of extracted modes.
    pub fn len(&self) -> usize {
        self.frequency.len()
    }

    /// Returns true when no modes are present.
    pub fn is_empty(&self) -> bool {
        self.frequency.is_empty()
    }

    /// Converts the table to a dense `(n_modes, 4)` matrix.
    pub fn to_array(&self) -> Array2<f64> {
        let n = self.len();
        let mut out = Array2::<f64>::zeros((n, 4));
        out.column_mut(0).assign(&self.frequency);
        out.column_mut(1).assign(&self.damping);
        out.column_mut(2).assign(&self.wavelength);
        out.column_mut(3).assign(&self.magnitude);
        out
    }

    /// Converts the table into a named column map.
    pub fn to_map(&self) -> HashMap<String, Array1<f64>> {
        HashMap::from([
            ("Frequency".to_string(), self.frequency.clone()),
            ("Damping".to_string(), self.damping.clone()),
            ("Wavelength".to_string(), self.wavelength.clone()),
            ("Magnitude".to_string(), self.magnitude.clone()),
        ])
    }
}

#[derive(Debug, Clone)]
/// Detected peaks in SGMA spectra.
pub struct Peaks {
    /// Wavelength coordinate for each detected peak.
    pub wavelength: Array1<f64>,
    /// Frequency coordinate for each detected peak.
    pub frequency: Array1<f64>,
    /// Peak magnitude.
    pub magnitude: Array1<f64>,
    /// Optional scale index for each peak.
    pub scale_idx: Option<Array1<usize>>,
    /// Optional frequency index for each peak.
    pub freq_idx: Option<Array1<usize>>,
}

impl Peaks {
    /// Returns an empty peak set.
    pub fn empty(with_indices: bool) -> Self {
        Self {
            wavelength: Array1::zeros(0),
            frequency: Array1::zeros(0),
            magnitude: Array1::zeros(0),
            scale_idx: with_indices.then(|| Array1::zeros(0)),
            freq_idx: with_indices.then(|| Array1::zeros(0)),
        }
    }
}

#[derive(Debug, Clone)]
/// Flattened peaks across multiple buses.
pub struct PeakCloud {
    /// Wavelength coordinate for each peak.
    pub wavelength: Array1<f64>,
    /// Frequency coordinate for each peak.
    pub frequency: Array1<f64>,
    /// Peak magnitude.
    pub magnitude: Array1<f64>,
    /// Source bus index.
    pub bus_id: Array1<usize>,
}

impl PeakCloud {
    /// Returns an empty peak cloud.
    pub fn empty() -> Self {
        Self {
            wavelength: Array1::zeros(0),
            frequency: Array1::zeros(0),
            magnitude: Array1::zeros(0),
            bus_id: Array1::zeros(0),
        }
    }
}

#[derive(Debug, Clone)]
/// KDE cluster centers for peak clouds.
pub struct ClusterCloud {
    /// Cluster wavelength center.
    pub wavelength: Array1<f64>,
    /// Cluster frequency center.
    pub frequency: Array1<f64>,
    /// Estimated density at each cluster center.
    pub density: Array1<f64>,
}

impl ClusterCloud {
    /// Returns an empty cluster cloud.
    pub fn empty() -> Self {
        Self {
            wavelength: Array1::zeros(0),
            frequency: Array1::zeros(0),
            density: Array1::zeros(0),
        }
    }
}

#[derive(Debug, Clone)]
/// Result of multi-bus SGMA analysis.
pub struct NetworkAnalysisResult {
    /// All detected peaks across buses.
    pub peaks: PeakCloud,
    /// Density clusters computed from the peak cloud.
    pub clusters: ClusterCloud,
}

impl NetworkAnalysisResult {
    /// Returns an empty multi-bus SGMA analysis result.
    pub fn empty() -> Self {
        Self {
            peaks: PeakCloud::empty(),
            clusters: ClusterCloud::empty(),
        }
    }
}