holos-tda 0.9.0

Vietoris-Rips persistence and checked degree-Rips modules
Documentation
use crate::{
    AtlasArtifact, EdgeKey, SparseDistanceMatrix, TopologyEvent, TopologyEventKind, UpdateMode,
};

use super::model::{TrajectoryError, TrajectoryStep};
use super::primitives::{put_optional_f64, put_u16, put_u64, put_usize};
use super::{F64_BITS_CODEC, MAGIC, WIRE_VERSION};

pub(super) fn encode_atlas(atlas: &AtlasArtifact) -> Result<Vec<u8>, TrajectoryError> {
    atlas
        .encode()
        .map_err(|error| TrajectoryError::new(error.to_string()))
}

pub(super) fn encode_checkpoints(
    steps: &[TrajectoryStep],
) -> Result<Vec<Option<Vec<u8>>>, TrajectoryError> {
    let mut checkpoints = Vec::with_capacity(steps.len());
    for step in steps {
        checkpoints.push(step.checkpoint.as_ref().map(encode_atlas).transpose()?);
    }
    Ok(checkpoints)
}

pub(super) fn encode_trajectory_header(
    out: &mut Vec<u8>,
    step_count: usize,
    initial_atlas_bytes: usize,
) -> Result<(), TrajectoryError> {
    out.extend_from_slice(MAGIC);
    put_u16(out, WIRE_VERSION);
    out.push(F64_BITS_CODEC);
    put_usize(out, step_count, "step count")?;
    put_usize(out, initial_atlas_bytes, "initial atlas byte count")?;
    Ok(())
}

pub(super) fn encode_trajectory_step(
    out: &mut Vec<u8>,
    step: &TrajectoryStep,
    checkpoint: Option<&[u8]>,
) -> Result<(), TrajectoryError> {
    encode_graph(out, &step.input)?;
    out.push(mode_tag(step.mode));
    put_usize(out, step.events.len(), "event count")?;
    encode_events(out, &step.events)?;
    put_usize(
        out,
        checkpoint.map_or(0, <[u8]>::len),
        "checkpoint byte count",
    )?;
    if let Some(checkpoint) = checkpoint {
        out.extend_from_slice(checkpoint);
    }
    Ok(())
}

fn encode_events(out: &mut Vec<u8>, events: &[TopologyEvent]) -> Result<(), TrajectoryError> {
    for event in events {
        encode_event(out, event)?;
    }
    Ok(())
}

pub(super) fn encode_graph(
    out: &mut Vec<u8>,
    input: &SparseDistanceMatrix,
) -> std::result::Result<(), TrajectoryError> {
    let edges: Vec<_> = input.edges().collect();
    put_usize(out, input.len(), "graph vertex count")?;
    put_usize(out, edges.len(), "graph edge count")?;
    for (u, v, value) in edges {
        put_usize(out, u, "edge endpoint")?;
        put_usize(out, v, "edge endpoint")?;
        put_u64(out, value.to_bits());
    }
    Ok(())
}

fn encode_event(
    out: &mut Vec<u8>,
    event: &TopologyEvent,
) -> std::result::Result<(), TrajectoryError> {
    out.push(event_kind_tag(event.kind));
    put_optional_edge(out, event.first)?;
    put_optional_edge(out, event.second)?;
    put_optional_f64(out, event.old_first);
    put_optional_f64(out, event.new_first);
    put_optional_f64(out, event.old_second);
    put_optional_f64(out, event.new_second);
    Ok(())
}

fn mode_tag(mode: UpdateMode) -> u8 {
    match mode {
        UpdateMode::Reused => 0,
        UpdateMode::Recomputed => 1,
    }
}

fn event_kind_tag(kind: TopologyEventKind) -> u8 {
    match kind {
        TopologyEventKind::VertexSetChanged => 0,
        TopologyEventKind::EdgeSetChanged => 1,
        TopologyEventKind::ThresholdCrossing => 2,
        TopologyEventKind::EqualitySplit => 3,
        TopologyEventKind::EqualityMerge => 4,
        TopologyEventKind::OrderSwap => 5,
    }
}

fn put_optional_edge(
    out: &mut Vec<u8>,
    edge: Option<EdgeKey>,
) -> std::result::Result<(), TrajectoryError> {
    match edge {
        None => out.push(0),
        Some(edge) => {
            out.push(1);
            put_usize(out, edge.u, "event edge endpoint")?;
            put_usize(out, edge.v, "event edge endpoint")?;
        }
    }
    Ok(())
}