holos-tda 0.9.0

Vietoris-Rips persistence and checked degree-Rips modules
Documentation
use super::*;
use crate::{
    CoverageAction, CoverageFence, CoverageLimits, CoverageSpecification, CoverageState,
    CoverageSynthesisLimits, PlanarCoverageModel, SparseDistanceMatrix,
};

fn two_state_problem() -> (CoverageSpecification, Vec<CoverageAction>) {
    let graph = SparseDistanceMatrix::from_triplets(
        6,
        &[
            (0, 1, 1.0),
            (1, 2, 1.0),
            (2, 3, 1.0),
            (0, 3, 1.0),
            (0, 4, 1.0),
            (1, 4, 1.0),
            (2, 4, 1.0),
            (3, 4, 1.0),
            (0, 5, 1.0),
            (1, 5, 1.0),
            (2, 5, 1.0),
            (3, 5, 1.0),
        ],
    )
    .unwrap();
    let state = CoverageState::new(0, 0, &graph, vec![0, 1, 2, 3], 1.0).unwrap();
    let specification = CoverageSpecification::new(
        6,
        PlanarCoverageModel::new(1.0, 1.0).unwrap(),
        2,
        CoverageFence::new(vec![0, 1, 2, 3]).unwrap(),
        vec![4, 5],
        0,
        vec![
            state.clone(),
            CoverageState::new(0, 1, &graph, state.base_vertices().to_vec(), 1.0).unwrap(),
        ],
        CoverageLimits::default(),
    )
    .unwrap();
    let actions = vec![
        CoverageAction::new(4, 7, vec![0]),
        CoverageAction::new(5, 3, vec![1]),
    ];
    (specification, actions)
}

#[test]
fn independent_frontiers_compose_under_one_global_limit() {
    let (specification, actions) = two_state_problem();
    let infeasible = compose_coverage_frontiers(
        &specification,
        &actions,
        1,
        CoverageSynthesisLimits::default(),
    )
    .unwrap();
    assert_eq!(infeasible.status(), CoverageCompositionStatus::Infeasible);

    let optimal = compose_coverage_frontiers(
        &specification,
        &actions,
        2,
        CoverageSynthesisLimits::default(),
    )
    .unwrap();
    assert_eq!(optimal.status(), CoverageCompositionStatus::Optimal);
    assert_eq!(optimal.cost(), Some(10));
    assert_eq!(optimal.selected(), &[0, 1]);
    assert_eq!(optimal.frontiers().len(), 2);
}

#[test]
fn a_local_work_limit_never_returns_an_optimal_claim() {
    let (specification, actions) = two_state_problem();
    let result = compose_coverage_frontiers(
        &specification,
        &actions,
        2,
        CoverageSynthesisLimits::default().with_max_oracle_calls(1),
    )
    .unwrap();
    assert_eq!(result.status(), CoverageCompositionStatus::SearchIncomplete);
    assert!(result.cost().is_none());
    assert!(result.selected().is_empty());
}

#[test]
fn proof_artifacts_use_the_composed_incumbent() {
    let (specification, actions) = two_state_problem();
    let artifact = crate::CoverageSynthesisArtifact::build(
        specification,
        actions,
        2,
        CoverageSynthesisLimits::default(),
    )
    .unwrap();
    assert_eq!(artifact.status(), crate::CoverageSynthesisStatus::Optimal);
    assert_eq!(artifact.selected(), &[0, 1]);
    assert_eq!(artifact.upper_bound_cost(), Some(10));
    assert!(artifact.proof_nodes() > 0);
}