aprender-graph 0.29.0

GPU-first embedded graph database for code analysis (call graphs, dependencies, AST traversals)
// GPU BFS Compute Shader
// Based on Gunrock (Wang et al., ACM ToPC 2017) frontier-based traversal
//
// Algorithm:
// 1. Start with source node in frontier
// 2. For each vertex in frontier:
//    - Mark all unvisited neighbors as visited
//    - Add neighbors to next frontier
// 3. Swap frontiers and repeat until empty

struct GraphData {
    num_nodes: u32,
    num_edges: u32,
}

@group(0) @binding(0) var<storage, read> graph_meta: GraphData;
@group(0) @binding(1) var<storage, read> row_offsets: array<u32>;
@group(0) @binding(2) var<storage, read> col_indices: array<u32>;
@group(0) @binding(3) var<storage, read_write> visited: array<atomic<u32>>;
@group(0) @binding(4) var<storage, read_write> distances: array<u32>;
@group(0) @binding(5) var<storage, read> frontier: array<u32>;
@group(0) @binding(6) var<storage, read_write> next_frontier: array<u32>;
@group(0) @binding(7) var<storage, read_write> next_frontier_size: atomic<u32>;

// BFS kernel - process one frontier vertex per thread
@compute @workgroup_size(256)
fn bfs_kernel(@builtin(global_invocation_id) global_id: vec3<u32>) {
    let tid = global_id.x;
    let frontier_size = arrayLength(&frontier);

    // Bounds check
    if (tid >= frontier_size) {
        return;
    }

    // Get current vertex from frontier
    let vertex = frontier[tid];
    if (vertex >= graph_meta.num_nodes) {
        return;
    }

    // CB-001: bounds-check row_offsets and distances access
    let ro_len = arrayLength(&row_offsets);
    let dist_len = arrayLength(&distances);
    if (vertex >= ro_len || (vertex + 1u) >= ro_len || vertex >= dist_len) {
        return;
    }

    let start = row_offsets[vertex];
    let end = row_offsets[vertex + 1u];
    if (vertex >= arrayLength(&distances)) {
        return;
    }
    let current_dist = distances[vertex];

    let ci_len = arrayLength(&col_indices);
    let nf_len = arrayLength(&next_frontier);
    let vis_len = arrayLength(&visited);
    for (var i = start; i < end; i++) {
        if (i >= ci_len) {
            break;
        }
        let neighbor = col_indices[i];

        if (neighbor >= graph_meta.num_nodes || neighbor >= vis_len) {
            continue;
        }

        if (neighbor >= arrayLength(&visited)) {
            continue;
        }
        let was_visited = atomicExchange(&visited[neighbor], 1u);

        if (was_visited == 0u) {
            if (neighbor < dist_len) {
                distances[neighbor] = current_dist + 1u;
            }

            // Add to next frontier (atomic counter)
            let next_idx = atomicAdd(&next_frontier_size, 1u);
            if (next_idx < nf_len) {
                next_frontier[next_idx] = neighbor;
            }
        }
    }
}