holos-tda 0.9.0

Vietoris-Rips persistence and checked degree-Rips modules
Documentation
use crate::program::class_continuation;
use crate::{
    CertificateLimits, ClassCorrespondence, PersistenceProgram, ProgramArtifact, ProgramUpdateMode,
    SparseDistanceMatrix,
};

use super::codec::{diagram_bits_equal, program_artifact_error};
use super::model::{ProgramTraceError, ProgramTraceStep};

pub(crate) fn verify_program_artifact(
    artifact: &ProgramArtifact,
    graph: &SparseDistanceMatrix,
    certificate_limits: CertificateLimits,
) -> Result<PersistenceProgram, ProgramTraceError> {
    artifact
        .verify(graph, certificate_limits)
        .map_err(program_artifact_error)
}

pub(crate) fn replay_step(
    program: &mut PersistenceProgram,
    step: &ProgramTraceStep,
    index: usize,
    certificate_limits: CertificateLimits,
) -> Result<crate::ProgramUpdate, ProgramTraceError> {
    if step.mode == ProgramUpdateMode::Reused {
        return program
            .advance_reused(&step.graph)
            .map_err(|error| ProgramTraceError::new(error.to_string()));
    }
    replay_checkpoint_step(program, step, index, certificate_limits)
}

pub(crate) fn replay_checkpoint_step(
    program: &mut PersistenceProgram,
    step: &ProgramTraceStep,
    index: usize,
    certificate_limits: CertificateLimits,
) -> Result<crate::ProgramUpdate, ProgramTraceError> {
    let checkpoint = step.checkpoint.as_ref().ok_or_else(|| {
        ProgramTraceError::new(format!("step {index} has no required checkpoint"))
    })?;
    let replacement = verify_program_artifact(checkpoint, &step.graph, certificate_limits)?;
    let (mode, events, work) = program
        .preview_update(&step.graph, replacement.states().len())
        .map_err(|error| ProgramTraceError::new(error.to_string()))?;
    let continuation = class_continuation(&program.result().spaces, &replacement.result().spaces);
    let correspondence = replay_correspondence(program, &step.graph, &replacement)?;
    let result = replacement.result().clone();
    *program = replacement;
    Ok(crate::ProgramUpdate {
        result,
        mode,
        events,
        continuation,
        correspondence,
        work,
    })
}

pub(crate) fn replay_correspondence(
    program: &PersistenceProgram,
    graph: &SparseDistanceMatrix,
    replacement: &PersistenceProgram,
) -> Result<Vec<ClassCorrespondence>, ProgramTraceError> {
    crate::class_correspondences(
        program.current_graph(),
        &program.result().spaces,
        graph,
        &replacement.result().spaces,
        replacement.params().modulus,
    )
    .map_err(|error| ProgramTraceError::new(error.to_string()))
}

pub(crate) fn check_replayed_step(
    checked: &crate::ProgramUpdate,
    declared: &ProgramTraceStep,
    index: usize,
) -> Result<(), ProgramTraceError> {
    let difference = if checked.mode != declared.mode {
        Some("mode".to_string())
    } else if checked.work != declared.work {
        Some(format!(
            "work counters differ: declared {:?}, replayed {:?}",
            declared.work, checked.work
        ))
    } else if checked.events != declared.events {
        Some("events".to_string())
    } else if checked.continuation != declared.continuation {
        Some("class continuation".to_string())
    } else if checked.correspondence != declared.correspondence {
        Some("class correspondence".to_string())
    } else if !diagram_bits_equal(&checked.result.diagram, &declared.diagram) {
        Some("diagram".to_string())
    } else {
        None
    };
    if let Some(difference) = difference {
        return Err(ProgramTraceError::new(format!(
            "step {index} {difference} in independent replay"
        )));
    }
    Ok(())
}

pub(crate) fn check_step_shape(
    step: &ProgramTraceStep,
) -> std::result::Result<(), ProgramTraceError> {
    match (step.mode, step.checkpoint.is_some()) {
        (ProgramUpdateMode::Reused, false)
        | (ProgramUpdateMode::Repaired, true)
        | (ProgramUpdateMode::Recompiled, true) => Ok(()),
        (ProgramUpdateMode::Reused, true) => Err(ProgramTraceError::new(
            "a reused step contains a redundant checkpoint",
        )),
        (_, false) => Err(ProgramTraceError::new(
            "a repaired or recompiled step has no checkpoint",
        )),
    }
}