weirflow 0.1.0

GPU-first dataflow analysis primitives for Vyre and Santh compiler pipelines.
Documentation
use super::*;

#[test]
fn direct_ifds_prepare_bytes_record_only_cache_misses() {
    let mut batch = super::DirectResidentIfdsBatch::<usize>::with_max_entries(2);
    let dispatch = DirectSolveDispatch::new();
    let graph_key =
        DirectResidentIfdsGraphKey::from_edges(1, 4, 1, &[(0u32, 0u32, 1u32)], &[], &[], &[]);
    let seed_a = [(0u32, 0u32, 0u32)];
    let seed_b = [(0u32, 1u32, 0u32)];
    let mut results = Vec::new();

    batch
        .solve_many_with_graph_key_into(
            &dispatch,
            graph_key,
            1,
            4,
            1,
            &[(0u32, 0u32, 1u32)],
            &[],
            &[],
            &[],
            &[&seed_a[..], &seed_b[..]],
            4,
            &mut results,
        )
        .expect("first direct resident IFDS solve must prepare resident CSR");

    assert_eq!(results, vec![vec![0], vec![1024]]);
    let first_stats = batch.stats();
    assert_eq!(first_stats.misses, 1);
    assert_eq!(first_stats.hits, 0);
    assert_eq!(first_stats.direct_prepares, 1);
    assert_eq!(first_stats.resident_solve_batches, 1);
    assert_eq!(first_stats.resident_solve_queries, 2);
    assert_eq!(first_stats.resident_solve_non_empty_queries, 2);
    assert_eq!(first_stats.resident_dispatches, 1);
    assert_eq!(
        first_stats.resident_dispatches,
        first_stats.resident_solver.resident_dispatches,
        "direct IFDS facade stats must expose underlying resident dispatch batches without making callers dig into nested solver stats"
    );
    assert_eq!(
        first_stats.seed_upload_bytes,
        first_stats.resident_solver.seed_upload_bytes
    );
    assert_eq!(
        first_stats.result_readback_bytes,
        first_stats.resident_solver.result_readback_bytes
    );
    assert!(
        first_stats.seed_upload_bytes > 0 && first_stats.result_readback_bytes > 0,
        "direct IFDS batch stats must make seed upload and result readback bytes visible for benchmark evidence"
    );
    assert_eq!(
        first_stats.direct_prepare_bytes,
        first_stats.retained_bytes as u64
    );

    let cached_key =
        DirectResidentIfdsGraphKey::from_edges(1, 4, 1, &[(0u32, 0u32, 1u32)], &[], &[], &[]);
    batch
        .solve_cached_with_graph_key_into(&dispatch, cached_key, &[&seed_b[..]], 4, &mut results)
        .expect("cached direct resident IFDS solve must reuse prepared resident CSR");

    assert_eq!(results, vec![vec![1024]]);
    let second_stats = batch.stats();
    assert_eq!(second_stats.hits, 1);
    assert_eq!(second_stats.misses, 1);
    assert_eq!(second_stats.direct_prepares, 1);
    assert_eq!(second_stats.resident_solve_batches, 2);
    assert_eq!(second_stats.resident_solve_queries, 3);
    assert_eq!(second_stats.resident_solve_non_empty_queries, 3);
    assert_eq!(second_stats.resident_dispatches, 2);
    assert!(
        second_stats.seed_upload_bytes > first_stats.seed_upload_bytes
            && second_stats.result_readback_bytes > first_stats.result_readback_bytes,
        "cached direct IFDS solves must still report their per-query upload/readback movement"
    );
    assert_eq!(
        second_stats.direct_prepare_bytes, first_stats.direct_prepare_bytes,
        "cached-key direct IFDS hot path must not count another resident CSR prepare"
    );
    assert_eq!(
        second_stats.resident_graph_reuse_telemetry(),
        vyre::ResidentGraphReuseTelemetry::from_counters(
            1,
            1,
            first_stats.direct_prepare_bytes,
            first_stats.direct_prepare_bytes
        ),
        "direct IFDS repeated graph solves must use shared resident graph reuse telemetry"
    );
    assert_eq!(
            dispatch.dispatches.get(),
            8,
            "resident parallel IFDS fake dispatch should launch the bounded iteration window for each solve"
        );
}

#[test]
fn direct_ifds_graph_view_reuses_hash_for_prepare_or_hit_path() {
    let mut batch = super::DirectResidentIfdsBatch::<usize>::with_max_entries(2);
    let dispatch = DirectSolveDispatch::new();
    let intra = [(0u32, 0u32, 1u32)];
    let graph = DirectResidentIfdsGraph::from_edges(1, 4, 1, &intra, &[], &[], &[]);
    let seed_a = [(0u32, 0u32, 0u32)];
    let seed_b = [(0u32, 1u32, 0u32)];
    let mut results = Vec::new();

    batch
        .solve_many_with_graph_view_into(&dispatch, graph, &[&seed_a[..]], 4, &mut results)
        .expect("graph-view direct resident IFDS solve must prepare resident CSR");
    assert_eq!(results, vec![vec![0]]);

    batch
        .solve_many_with_graph_view_into(&dispatch, graph, &[&seed_b[..]], 4, &mut results)
        .expect("graph-view direct resident IFDS solve must reuse resident CSR");
    assert_eq!(results, vec![vec![1024]]);

    let stats = batch.stats();
    assert_eq!(stats.misses, 1);
    assert_eq!(stats.hits, 1);
    assert_eq!(stats.direct_prepares, 1);
    assert_eq!(
            stats.direct_prepare_bytes,
            stats.retained_bytes as u64,
            "graph-view repeated solves must account retained direct CSR bytes only for the single cache miss"
        );
    assert_eq!(
        batch.resident_graph_reuse_telemetry(),
        vyre::ResidentGraphReuseTelemetry::from_counters(
            1,
            1,
            stats.direct_prepare_bytes,
            stats.direct_prepare_bytes
        ),
        "graph-view repeated direct IFDS solves must expose shared graph reuse telemetry"
    );
}

#[test]
fn direct_ifds_graph_view_single_solve_routes_without_rehashing_edges() {
    let mut batch = super::DirectResidentIfdsBatch::<usize>::with_max_entries(2);
    let dispatch = DirectSolveDispatch::new();
    let intra = [(0u32, 0u32, 1u32)];
    let graph = DirectResidentIfdsGraph::from_edges(1, 4, 1, &intra, &[], &[], &[]);
    let seed_a = [(0u32, 0u32, 0u32)];
    let seed_b = [(0u32, 1u32, 0u32)];
    let mut result = Vec::new();

    batch
        .solve_with_graph_view_into(&dispatch, graph, &seed_a, 4, &mut result)
        .expect("graph-view single direct IFDS solve must prepare resident CSR");
    assert_eq!(result, vec![0]);

    batch
        .solve_with_graph_view_into(&dispatch, graph, &seed_b, 4, &mut result)
        .expect("graph-view single direct IFDS solve must reuse resident CSR");
    assert_eq!(result, vec![1024]);

    let stats = batch.stats();
    assert_eq!(stats.misses, 1);
    assert_eq!(stats.hits, 1);
    assert_eq!(stats.direct_prepares, 1);
    assert_eq!(
        stats.direct_prepare_bytes, stats.retained_bytes as u64,
        "graph-view single hot path must retain the direct CSR after one prepare"
    );
}

#[test]
fn direct_ifds_cached_single_key_requires_no_edge_slices_after_warmup() {
    let mut batch = super::DirectResidentIfdsBatch::<usize>::with_max_entries(2);
    let dispatch = DirectSolveDispatch::new();
    let intra = [(0u32, 0u32, 1u32)];
    let graph = DirectResidentIfdsGraph::from_edges(1, 4, 1, &intra, &[], &[], &[]);
    let seed_a = [(0u32, 0u32, 0u32)];
    let seed_b = [(0u32, 1u32, 0u32)];
    let mut result = Vec::new();

    batch
        .solve_with_graph_view_into(&dispatch, graph, &seed_a, 4, &mut result)
        .expect("graph-view warmup must prepare the resident graph");
    assert_eq!(result, vec![0]);

    batch
        .solve_cached_single_with_graph_key_into(&dispatch, graph.key(), &seed_b, 4, &mut result)
        .expect("cached-key single solve must reuse the warmed resident graph");
    assert_eq!(result, vec![1024]);

    let stats = batch.stats();
    assert_eq!(stats.misses, 1);
    assert_eq!(stats.hits, 1);
    assert_eq!(stats.direct_prepares, 1);
    assert_eq!(
        stats.direct_prepare_bytes, stats.retained_bytes as u64,
        "cached single-key hot path must not prepare or account another direct CSR"
    );
}