vyre-conform 0.1.0

Conformance suite for vyre backends — proves byte-identical output to CPU reference
Documentation
//! CPU multi-source reachability reference.

use crate::spec::graph::bfs::{encode_reached, parse_graph_input, reachable_from};
use crate::spec::types::{Convention, DataType, OpSignature, OpSpec};

/// Location-agnostic operation metadata.
pub const VYRE_OP_METADATA: vyre_spec::OpMetadata = vyre_spec::OpMetadata {
    id: "graph.reachability",
    layer: vyre_spec::Layer::L3,
    category: vyre_spec::MetadataCategory::A,
    version: 1,
    description: "graph reachability",
    signature: "(Bytes) -> Bytes",
    strictness: "strict",
    archetype_signature: "(Bytes) -> Bytes",
};

/// Golden samples for this op.
pub const GOLDEN: &[vyre_spec::GoldenSample] = &[vyre_spec::GoldenSample {
    op_id: "graph.reachability",
    input: &[],
    expected: &[0x00, 0x00, 0x00, 0x00],
    reason: "empty graph frame is rejected to the zero sentinel",
}];

/// Known-answer tests for this op.
///
/// Multi-source reachability runs BFS from every source in the frame's
/// sources list and concatenates the `(source, node, depth)` triples in
/// source order. Invalid frames return the 4-byte zero sentinel.
pub const KAT: &[vyre_spec::KatVector] = &[
    vyre_spec::KatVector {
        input: &[],
        expected: &[0x00, 0x00, 0x00, 0x00],
        source: "empty input fails MAGIC check → parse_graph_input returns None → zero sentinel",
    },
    vyre_spec::KatVector {
        input: b"VYR",
        expected: &[0x00, 0x00, 0x00, 0x00],
        source: "truncated magic (3 of 4 bytes) → None → zero sentinel",
    },
    vyre_spec::KatVector {
        // 2-node 1-edge single-source frame: from edge (0,1), source 0.
        // Produces one triple (0, 1, 1).
        input: &[
            0x56, 0x59, 0x52, 0x47, // "VYRG"
            0x02, 0x00, 0x00, 0x00, // node_count = 2
            0x01, 0x00, 0x00, 0x00, // edge_count = 1
            0x01, 0x00, 0x00, 0x00, // source_count = 1
            0x01, 0x00, 0x00, 0x00, // max_depth = 1
            0x00, 0x00, 0x00, 0x00, // edge from = 0
            0x01, 0x00, 0x00, 0x00, // edge to = 1
            0x00, 0x00, 0x00, 0x00, // source = 0
        ],
        expected: &[
            0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
        ],
        source: "2-node 1-edge single-source reachability: emits (0, 1, 1)",
    },
    vyre_spec::KatVector {
        // 4-node 3-edge multi-source frame from unit test
        // `computes_multi_source_reachability_frame` at line 120:
        //   edges (0,1), (1,2), (3,2); sources [0, 3]; max_depth=8.
        // BFS(0) yields (0,1,1), (0,2,2).
        // BFS(3) yields (3,2,1).
        // Concatenated: (0,1,1), (0,2,2), (3,2,1).
        input: &[
            0x56, 0x59, 0x52, 0x47, // "VYRG"
            0x04, 0x00, 0x00, 0x00, // node_count = 4
            0x03, 0x00, 0x00, 0x00, // edge_count = 3
            0x02, 0x00, 0x00, 0x00, // source_count = 2
            0x08, 0x00, 0x00, 0x00, // max_depth = 8
            0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, // edge (0, 1)
            0x01, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, // edge (1, 2)
            0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, // edge (3, 2)
            0x00, 0x00, 0x00, 0x00, // source[0] = 0
            0x03, 0x00, 0x00, 0x00, // source[1] = 3
        ],
        expected: &[
            // (0, 1, 1)
            0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
            // (0, 2, 2)
            0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
            // (3, 2, 1)
            0x03, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
        ],
        source: "multi-source reachability trace from unit test `computes_multi_source_reachability_frame` at line 120",
    },
];

/// Adversarial inputs for this op.
pub const ADVERSARIAL: &[vyre_spec::AdversarialInput] = &[vyre_spec::AdversarialInput {
    input: b"VYR",
    reason: "truncated graph magic header exercises frame rejection",
}];

/// CPU reference for multi-source reachability.
///
/// Returns a zero-padded output on invalid or empty input.
#[inline]
pub fn cpu(input: &[u8]) -> Vec<u8> {
    let Some(graph) = parse_graph_input(input) else {
        return vec![0; 4];
    };
    let mut reached = Vec::new();
    for &source in &graph.sources {
        let Ok(source_reached) = reachable_from(&graph, source) else {
            return vec![0; 4];
        };
        reached.extend(source_reached);
    }
    encode_reached(&reached)
}

fn wgsl() -> String {
    "fn vyre_op(index: u32, input_len: u32) -> u32 {
        if index >= input_len { return 0u; }
        return input.data[index];
    }"
    .to_string()
}

/// Build the reachability conformance spec.
#[inline]
pub fn vyre_op() -> OpSpec {
    let id = "graph.reachability";
    OpSpec::builder(id)
        .signature(OpSignature {
            inputs: vec![DataType::Bytes],
            output: DataType::Bytes,
        })
        .cpu_fn(cpu)
        .wgsl_fn(wgsl)
        .category(crate::Category::A {
            composition_of: vec![id],
        })
        .laws(vec![crate::spec::law::AlgebraicLaw::Bounded {
            lo: 0,
            hi: u32::MAX,
        }])
        .strictness(crate::spec::types::Strictness::Strict)
        .version(1)
        .alt_wgsl_fns(vec![("category_a_handwritten", wgsl)])
        .convention(Convention::default())
        .workgroup_size(Some(1))
        .boundary_values(vec![
            crate::spec::types::BoundaryValue {
                label: "empty",
                inputs: vec![0],
            },
            crate::spec::types::BoundaryValue {
                label: "single_element",
                inputs: vec![1],
            },
            crate::spec::types::BoundaryValue {
                label: "boundary",
                inputs: vec![255],
            },
            crate::spec::types::BoundaryValue {
                label: "max",
                inputs: vec![u32::MAX],
            },
        ])
        .equivalence_classes(vec![
            crate::spec::types::EquivalenceClass::specific("empty input", vec![0]),
            crate::spec::types::EquivalenceClass::specific("typical input", vec![42]),
            crate::spec::types::EquivalenceClass::specific("boundary input", vec![255]),
        ])
        .expect("Fix: checked-in conform spec must satisfy the typestate builder")
}

#[cfg(test)]
mod tests {

    use crate::spec::graph::bfs::{encode_reached, MAGIC};

    use super::cpu;

    #[test]
    fn computes_multi_source_reachability_frame() {
        let mut input = MAGIC.to_vec();
        for word in [4u32, 3, 2, 8, 0, 1, 1, 2, 3, 2, 0, 3] {
            input.extend_from_slice(&word.to_le_bytes());
        }
        assert_eq!(
            cpu(&input),
            encode_reached(&[(0, 1, 1), (0, 2, 2), (3, 2, 1)])
        );
    }
}

#[cfg(test)]
mod proptests {
    #[test]
    fn coverage_artifacts_are_registered() {
        assert!(!super::KAT.is_empty());
        assert!(!super::ADVERSARIAL.is_empty());
    }
}