holos-tda 0.9.0

Vietoris-Rips persistence and checked degree-Rips modules
Documentation
use super::*;

#[test]
fn coverage_cli_certifies_failures_and_warns_about_geometry() {
    let graph = TempFile::new(
        "coverage.spr",
        "0 1 1\n1 2 1\n2 3 1\n0 3 1\n0 4 1\n1 4 1\n2 4 1\n3 4 1\n0 5 1\n1 5 1\n2 5 1\n3 5 1\n",
    );
    let artifact = TempFile::new("coverage.hcov", "");
    let out = run(&[
        "cover",
        artifact.path().to_str().unwrap(),
        "--state",
        graph.path().to_str().unwrap(),
        "--vertices",
        "6",
        "--broadcast-radius",
        "1",
        "--sensing-radius",
        "1",
        "--fence",
        "0,1,2,3",
        "--failable",
        "4,5",
        "--failure-budget",
        "1",
        "--candidate",
        "4",
        "2",
        "all",
        "--candidate",
        "5",
        "3",
        "all",
        "--max-activations",
        "2",
        "--modulus",
        "3",
    ]);
    assert!(out.status.success(), "stderr: {}", stderr(&out));
    assert!(stdout(&out).contains("relative coverage"));
    assert!(stderr(&out).contains("physical coverage requires"));
    let bytes = std::fs::read(artifact.path()).unwrap();
    assert!(is_coverage(&bytes));
    let checked = verify_coverage(&bytes, ProofLimits::default()).unwrap();
    assert_eq!(checked.selected, 2);
    assert_eq!(checked.total_cost, Some(5));
    assert_eq!(checked.failure_budget, 1);
}

#[test]
fn collapse_portfolio_cli_selects_and_encodes_every_schedule() {
    let graph = TempFile::new(
        "portfolio.spr",
        "0 1 1\n0 2 1\n0 3 1\n1 2 1\n1 3 1\n2 3 1\n",
    );
    let artifact = TempFile::new("portfolio.hpor", "");
    let out = run(&[
        "collapse-portfolio",
        graph.path().to_str().unwrap(),
        artifact.path().to_str().unwrap(),
        "--threads",
        "2",
    ]);
    assert!(out.status.success(), "stderr: {}", stderr(&out));
    assert!(stdout(&out).contains("selected candidate"));
    let bytes = std::fs::read(artifact.path()).unwrap();
    let decoded = CollapsePortfolioArtifact::decode(
        &bytes,
        CollapsePortfolioLimits::default(),
        CollapsePortfolioDecodeLimits::default(),
    )
    .unwrap();
    let input = holos_tda::io::read_sparse_matrix(graph.path(), 1).unwrap();
    decoded
        .verify_sparse(&input, None, CollapsePortfolioLimits::default())
        .unwrap();
    assert_eq!(decoded.entries().len(), 3);
}

#[test]
fn coverage_cli_binds_finite_states_to_planar_coordinates() {
    let graph = TempFile::new(
        "coverage_geometry.spr",
        "0 1 2\n1 2 2\n2 3 2\n0 3 2\n0 4 1.4142135623730951\n1 4 1.4142135623730951\n2 4 1.4142135623730951\n3 4 1.4142135623730951\n",
    );
    let coordinates = TempFile::new("coverage_geometry.pts", "0 0\n2 0\n2 2\n0 2\n1 1\n");
    let artifact = TempFile::new("coverage_geometry.hgeo", "");
    let out = run(&[
        "cover",
        artifact.path().to_str().unwrap(),
        "--state",
        graph.path().to_str().unwrap(),
        "--coordinates",
        coordinates.path().to_str().unwrap(),
        "--vertices",
        "5",
        "--broadcast-radius",
        "2",
        "--sensing-radius",
        "2",
        "--fence",
        "0,1,2,3",
        "--candidate",
        "4",
        "1",
        "all",
        "--max-activations",
        "1",
    ]);
    assert!(out.status.success(), "stderr: {}", stderr(&out));
    assert!(!stderr(&out).contains("physical coverage requires"));
    let bytes = std::fs::read(artifact.path()).unwrap();
    assert!(is_geometry_bound_coverage(&bytes));
    let checked = verify_geometry_bound_coverage(&bytes, ProofLimits::default()).unwrap();
    assert_eq!(checked.vertices, 5);
    assert_eq!(checked.pair_checks, 10);
    assert_eq!(checked.coverage.total_cost, Some(1));
}

#[test]
fn circular_cli_accepts_ripser_terms_and_writes_a_checked_artifact() {
    let graph = TempFile::new(
        "circular.spr",
        "0 1 1\n1 2 1\n2 3 1\n3 4 1\n4 5 1\n5 6 1\n6 7 1\n0 7 1\n",
    );
    let cocycle = TempFile::new("circular.cocycle", "0 1 1\n");
    let artifact = TempFile::new("circular.hcc", "");
    let phases = TempFile::new("circular.phase", "");
    let out = run(&[
        "circular",
        graph.path().to_str().unwrap(),
        cocycle.path().to_str().unwrap(),
        artifact.path().to_str().unwrap(),
        "--at",
        "1",
        "--format",
        "sparse",
        "--phases",
        phases.path().to_str().unwrap(),
    ]);
    assert!(out.status.success(), "stderr: {}", stderr(&out));
    let bytes = std::fs::read(artifact.path()).unwrap();
    assert!(is_circular_coordinate(&bytes));
    let checked = verify_circular_coordinate(&bytes, CircularProofLimits::default()).unwrap();
    assert_eq!(checked.coordinates, 1);
    assert_eq!(
        std::fs::read_to_string(phases.path())
            .unwrap()
            .lines()
            .count(),
        8
    );
}

#[test]
fn circular_cli_consumes_a_graph_bound_persistent_class() {
    let graph = TempFile::new(
        "circular_class.spr",
        "0 1 1\n1 2 1\n2 3 1\n3 4 1\n4 5 1\n5 6 1\n6 7 1\n0 7 1\n",
    );
    let classes = TempFile::new("circular_classes.json", "");
    let artifact = TempFile::new("circular_class.hcc", "");
    let phases = TempFile::new("circular_class.phase", "");
    let computed = run(&[
        graph.path().to_str().unwrap(),
        "--format",
        "sparse",
        "--dim",
        "1",
        "--modulus",
        "47",
        "--representatives",
        classes.path().to_str().unwrap(),
    ]);
    assert!(computed.status.success(), "stderr: {}", stderr(&computed));

    let circular = run(&[
        "circular",
        graph.path().to_str().unwrap(),
        classes.path().to_str().unwrap(),
        artifact.path().to_str().unwrap(),
        "--class",
        "0",
        "0",
        "--format",
        "sparse",
        "--phases",
        phases.path().to_str().unwrap(),
    ]);
    assert!(circular.status.success(), "stderr: {}", stderr(&circular));
    let bytes = std::fs::read(artifact.path()).unwrap();
    assert!(is_circular_coordinate(&bytes));
    verify_circular_coordinate(&bytes, CircularProofLimits::default()).unwrap();

    let changed = TempFile::new(
        "circular_class_changed.spr",
        "0 1 0.5\n1 2 1\n2 3 1\n3 4 1\n4 5 1\n5 6 1\n6 7 1\n0 7 1\n",
    );
    let rejected = run(&[
        "circular",
        changed.path().to_str().unwrap(),
        classes.path().to_str().unwrap(),
        artifact.path().to_str().unwrap(),
        "--class",
        "0",
        "0",
        "--format",
        "sparse",
    ]);
    assert!(!rejected.status.success());
    assert!(
        stderr(&rejected).contains("different active graph"),
        "{}",
        stderr(&rejected)
    );
}

#[test]
fn affine_coverage_cli_binds_the_complete_schedule() {
    let trajectory = TempFile::new(
        "coverage.kin",
        "0 1 1 0\n1 2 1 0\n2 3 1 0\n0 3 1 0\n0 4 1 0\n1 4 1 0\n2 4 1 0\n3 4 1 0\n",
    );
    let artifact = TempFile::new("coverage_affine.hcov", "");
    let out = run(&[
        "cover-affine",
        trajectory.path().to_str().unwrap(),
        artifact.path().to_str().unwrap(),
        "--vertices",
        "5",
        "--start",
        "0",
        "--end",
        "1",
        "--broadcast-radius",
        "1",
        "--sensing-radius",
        "1",
        "--fence",
        "0,1,2,3",
        "--candidate",
        "4",
        "1",
        "all",
        "--max-activations",
        "1",
    ]);
    assert!(out.status.success(), "stderr: {}", stderr(&out));
    let bytes = std::fs::read(artifact.path()).unwrap();
    let checked = verify_coverage(&bytes, ProofLimits::default()).unwrap();
    assert_eq!(
        checked.source,
        holos_tda_check::VerifiedCoverageSource::Affine
    );
    assert_eq!(checked.states, 3);
    assert_eq!(checked.total_cost, Some(1));
}