use std::fmt;
use crate::{Diagram, Error, ExplainedDiagram, PersistentClassSpace, Result, RipsParams};
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct EdgeKey {
pub u: usize,
pub v: usize,
}
impl EdgeKey {
pub(crate) fn new(u: usize, v: usize) -> Self {
if u < v {
Self { u, v }
} else {
Self { u: v, v: u }
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct LineageId(pub(crate) [u8; 32]);
impl LineageId {
pub fn as_bytes(&self) -> &[u8; 32] {
&self.0
}
}
impl fmt::Display for LineageId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
for byte in self.0 {
write!(f, "{byte:02x}")?;
}
Ok(())
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[non_exhaustive]
pub enum EndpointGradient {
Edge(EdgeKey),
Tied(Vec<EdgeKey>),
Essential,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ClassSensitivity {
pub lineage: LineageId,
pub birth: EndpointGradient,
pub death: EndpointGradient,
}
#[derive(Debug, Clone, PartialEq)]
pub struct EvaluatedClassSpace {
pub lineage: LineageId,
pub space: PersistentClassSpace,
}
#[derive(Debug, Clone)]
pub struct AtlasEvaluation {
pub diagram: Diagram,
pub spaces: Vec<EvaluatedClassSpace>,
pub sensitivities: Vec<ClassSensitivity>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct TopologyEvent {
pub kind: TopologyEventKind,
pub first: Option<EdgeKey>,
pub second: Option<EdgeKey>,
pub old_first: Option<f64>,
pub new_first: Option<f64>,
pub old_second: Option<f64>,
pub new_second: Option<f64>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub enum TopologyEventKind {
VertexSetChanged,
EdgeSetChanged,
ThresholdCrossing,
EqualitySplit,
EqualityMerge,
OrderSwap,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum UpdateMode {
Reused,
Recomputed,
}
#[derive(Debug, Clone)]
pub struct AtlasUpdate {
pub atlas: PersistenceAtlas,
pub evaluation: AtlasEvaluation,
pub mode: UpdateMode,
pub events: Vec<TopologyEvent>,
}
#[derive(Debug, Clone)]
pub(crate) struct EndpointFormula {
pub(crate) sources: Vec<EdgeKey>,
}
impl EndpointFormula {
pub(crate) fn gradient(&self) -> EndpointGradient {
match self.sources.as_slice() {
[edge] => EndpointGradient::Edge(*edge),
edges => EndpointGradient::Tied(edges.to_vec()),
}
}
pub(crate) fn value(&self, topology: &[EdgeKey], values: &[f64]) -> Result<f64> {
let Some(first) = self.sources.first() else {
return Err(Error::InvalidInput(
"atlas endpoint has no controlling edge".into(),
));
};
let position = topology.binary_search(first).map_err(|_| {
Error::InvalidInput(format!(
"atlas endpoint edge ({}, {}) is absent",
first.u, first.v
))
})?;
let value = values[position];
for edge in &self.sources[1..] {
let position = topology.binary_search(edge).map_err(|_| {
Error::InvalidInput(format!(
"atlas endpoint edge ({}, {}) is absent",
edge.u, edge.v
))
})?;
let other = values[position];
if other.to_bits() != value.to_bits() {
return Err(Error::InvalidInput(
"atlas tied endpoint sources no longer agree".into(),
));
}
}
Ok(value)
}
}
#[derive(Debug, Clone)]
pub(crate) struct SpaceFormula {
pub(crate) lineage: LineageId,
pub(crate) birth: EndpointFormula,
pub(crate) death: Option<EndpointFormula>,
}
#[derive(Debug, Clone)]
pub struct PersistenceAtlas {
pub(crate) vertex_count: usize,
pub(crate) threshold: Option<f64>,
pub(crate) topology: Vec<EdgeKey>,
pub(crate) order: Vec<EdgeKey>,
pub(crate) order_positions: Vec<usize>,
pub(crate) values: Vec<f64>,
pub(crate) original_values: Vec<f64>,
pub(crate) input_digest: [u8; 32],
pub(crate) explained: ExplainedDiagram,
pub(crate) formulas: Vec<SpaceFormula>,
pub(crate) h0_deaths: Vec<EdgeKey>,
pub(crate) h0_essential: usize,
pub(crate) params: RipsParams,
}