use std::fmt;
use crate::collapse;
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Bar {
pub dim: usize,
pub birth: f64,
pub death: f64,
}
impl Bar {
pub fn is_essential(&self) -> bool {
self.death == f64::INFINITY
}
}
#[derive(Debug, Clone, Default)]
pub struct Diagram {
pub bars: Vec<Bar>,
}
impl Diagram {
pub fn in_dim(&self, dim: usize) -> impl Iterator<Item = &Bar> {
self.bars.iter().filter(move |b| b.dim == dim)
}
pub fn canonicalize(&mut self) {
self.bars.sort_by(|a, b| {
a.dim
.cmp(&b.dim)
.then(a.birth.total_cmp(&b.birth))
.then(a.death.total_cmp(&b.death))
});
}
}
#[derive(Debug, Clone)]
#[non_exhaustive]
pub struct RipsParams {
pub max_dim: usize,
pub threshold: Option<f64>,
pub modulus: u32,
pub threads: usize,
pub use_emergent_pairs: bool,
pub use_apparent_pairs: bool,
pub use_clearing: bool,
pub use_adjacency_rows: bool,
pub collapse_edges: bool,
pub collapse_schedule: CollapseSchedule,
pub adaptive_collapse: collapse::AdaptiveCollapseParams,
pub engine: Engine,
pub dense_storage: DenseStorage,
pub factorization: GraphFactorization,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[non_exhaustive]
pub enum Engine {
#[default]
Auto,
Dense,
Sparse,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[non_exhaustive]
pub enum DenseStorage {
#[default]
Auto,
Compact,
Square,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[non_exhaustive]
pub enum GraphFactorization {
Auto,
#[default]
Off,
Force,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[non_exhaustive]
pub enum CollapseSchedule {
#[default]
Serial,
Ordered,
Rounds,
Adaptive,
}
impl Default for RipsParams {
fn default() -> Self {
Self {
max_dim: 1,
threshold: None,
modulus: 2,
threads: 1,
use_emergent_pairs: true,
use_apparent_pairs: true,
use_clearing: true,
use_adjacency_rows: true,
collapse_edges: false,
collapse_schedule: CollapseSchedule::Serial,
adaptive_collapse: collapse::AdaptiveCollapseParams::default(),
engine: Engine::Auto,
dense_storage: DenseStorage::Auto,
factorization: GraphFactorization::Off,
}
}
}
impl RipsParams {
pub fn new(max_dim: usize) -> Self {
Self {
max_dim,
..Self::default()
}
}
pub fn with_threshold(mut self, threshold: f64) -> Self {
self.threshold = Some(threshold);
self
}
pub fn with_modulus(mut self, modulus: u32) -> Self {
self.modulus = modulus;
self
}
pub fn with_threads(mut self, threads: usize) -> Self {
self.threads = threads.max(1);
self
}
pub fn with_edge_collapse(mut self) -> Self {
self.collapse_edges = true;
self
}
pub fn with_collapse_schedule(mut self, schedule: CollapseSchedule) -> Self {
self.collapse_edges = true;
self.collapse_schedule = schedule;
self
}
pub fn with_adaptive_collapse(mut self, params: collapse::AdaptiveCollapseParams) -> Self {
self.collapse_edges = true;
self.collapse_schedule = CollapseSchedule::Adaptive;
self.adaptive_collapse = params;
self
}
pub fn with_engine(mut self, engine: Engine) -> Self {
self.engine = engine;
self
}
pub fn with_dense_storage(mut self, storage: DenseStorage) -> Self {
self.dense_storage = storage;
self
}
pub fn with_factorization(mut self, factorization: GraphFactorization) -> Self {
self.factorization = factorization;
self
}
}
#[derive(Debug, Clone, PartialEq)]
#[allow(missing_docs)]
pub enum Error {
InvalidDistance(String),
InvalidInput(String),
IndexOverflow { n: usize, dim: usize },
Io(String),
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Error::InvalidDistance(msg) => write!(f, "invalid distance: {msg}"),
Error::InvalidInput(msg) => write!(f, "invalid input: {msg}"),
Error::IndexOverflow { n, dim } => write!(
f,
"simplex index space overflows u64 for {n} points in dimension {dim}"
),
Error::Io(msg) => write!(f, "io error: {msg}"),
}
}
}
impl std::error::Error for Error {}
pub type Result<T> = std::result::Result<T, Error>;