Skip to content

Conversation

@georgiy-belyanin
Copy link
Member

@georgiy-belyanin georgiy-belyanin commented Aug 1, 2025

This patch introduces basic RPQ evaluation logic. It basically
interfaces the LAGraph (experimental) library that offers a collection
of the sparse linear algebra-based algorithms. It uses GraphBLAS [^1] as
a backend library for evaluating sparse matrix algebra. Rust <-> C
interfacing is done via FFI. Currently, the project requires our fork
[^2] since the required algorithm is not upstreamed yet. If you are
reading this and notice we occasionally have removed this from the fork
we are asking for an excuse and hope that you will be able to find the
suitable algorithm by yourself from the interfaces described on the Rust
side.

[^1] https://github.com/DrTimothyAldenDavis/GraphBLAS
[^2] https://github.com/SparseLinearAlgebra/LAGraph

Turns out there was an extra brace when parsing the graph edges due to
wrong bounds. Let's fix it.
This patch extends plain nvals information within the label in the
execution plan with label name. It is needed to match the actual
matrices and labels to generate complete plan.
@georgiy-belyanin georgiy-belyanin force-pushed the basic-eval branch 9 times, most recently from 9c0dc19 to 2a17a79 Compare August 3, 2025 07:22
@georgiy-belyanin georgiy-belyanin force-pushed the basic-eval branch 2 times, most recently from a13a26a to 89dd251 Compare November 19, 2025 12:21
@georgiy-belyanin georgiy-belyanin marked this pull request as ready for review November 19, 2025 12:23
This patch introduces basic RPQ evaluation logic. It basically
interfaces the LAGraph (experimental) library that offers a collection
of the sparse linear algebra-based algorithms. It uses GraphBLAS [^1] as
a backend library for evaluating sparse matrix algebra. Rust <-> C
interfacing is done via FFI. Currently, the project requires our fork
[^2] since the required algorithm is not upstreamed yet. If you are
reading this and notice we occasionally have removed this from the fork
we are asking for an excuse and hope that you will be able to find the
suitable algorithm by yourself from the interfaces described on the Rust
side.

[^1] https://github.com/DrTimothyAldenDavis/GraphBLAS
[^2] https://github.com/SparseLinearAlgebra/LAGraph
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR introduces basic Regular Path Query (RPQ) evaluation using the LAGraph library with GraphBLAS as the backend. The implementation adds FFI bindings to interface with C libraries and includes query execution with performance benchmarking capabilities.

Key changes:

  • Integration of LAGraph/GraphBLAS via FFI for sparse matrix operations
  • Implementation of RPQ evaluation logic with operator translation to matrix operations
  • Addition of randomized plan selection and performance measurement infrastructure

Reviewed Changes

Copilot reviewed 11 out of 13 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
vendor/LAGraph Added LAGraph as a git submodule dependency
src/query.rs Added Clone trait derivations to enable query cloning
src/plan.rs Introduced LabelMeta struct and replaced simple cost function with random selection
src/main.rs Added query execution loop with performance benchmarking and statistics collection
src/grb.rs Added GraphBLAS Matrix FFI wrapper
src/graph.rs Extended graph loading to include matrix files and vertex mappings, added error handling
src/eval.rs Implemented RPQ evaluation via LAGraph FFI bindings
build.rs Added linker configuration for LAGraph libraries
_typos.toml Configured typo checker to exclude vendor directory
Cargo.toml Added rand dependency
.gitmodules Configured LAGraph submodule
.github/workflows/ci.yml Added build steps for GraphBLAS and LAGraph dependencies

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

let cost_func = SillyCostFn;
let cost_func = CostFn;
let extractor = Extractor::new(&runner.egraph, cost_func);
extractor.find_best(runner.roots[0]).1.to_string()
Copy link

Copilot AI Nov 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Test uses undefined CostFn type. This should be RandomCostFn to match the actual implementation that replaced SillyCostFn.

Suggested change
extractor.find_best(runner.roots[0]).1.to_string()
let cost_func = RandomCostFn;

Copilot uses AI. Check for mistakes.
let mut splits = line.split_whitespace();
let edge = splits.next()?;
let edge = edge[1..edge.len()].to_string();
let edge = edge[1..edge.len() - 1].to_string();
Copy link

Copilot AI Nov 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

String slicing edge[1..edge.len() - 1] will panic if edge.len() is 0 or 1. Add length validation before slicing or use safer string manipulation methods.

Copilot uses AI. Check for mistakes.
.lines()
.filter_map(|line| {
let mut splits = line.split_whitespace();
let vert = splits.next()?;
Copy link

Copilot AI Nov 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

String slicing vert[1..vert.len() - 1] will panic if vert.len() is 0 or 1. Add length validation before slicing or use safer string manipulation methods.

Suggested change
let vert = splits.next()?;
let vert = splits.next()?;
if vert.len() < 2 {
return None;
}

Copilot uses AI. Check for mistakes.
Comment on lines +103 to +108
let mut mat: grb::Matrix = grb::Matrix(std::ptr::null_mut());
let mat = graph
.mats
.get(&meta.name)
.or({
graph.verts.get(&meta.name).map(|vert_idx| {
Copy link

Copilot AI Nov 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Variable mat is shadowed immediately after declaration. The mutable mat on line 103 is never used before being shadowed on line 104, making its initialization pointless. Remove the declaration on line 103 or rename one of the variables.

Suggested change
let mut mat: grb::Matrix = grb::Matrix(std::ptr::null_mut());
let mat = graph
.mats
.get(&meta.name)
.or({
graph.verts.get(&meta.name).map(|vert_idx| {
let mat = graph
.mats
.get(&meta.name)
.or({
graph.verts.get(&meta.name).map(|vert_idx| {
let mut mat: grb::Matrix = grb::Matrix(std::ptr::null_mut());

Copilot uses AI. Check for mistakes.
let runs: u32 = 1000;
let results: Vec<(RecExpr<Plan>, usize, Duration)> =
run_random(&graph, runs, &expr).collect();
let first_n_runs = runs / 100;
Copy link

Copilot AI Nov 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Integer division runs / 100 will be 0 when runs < 100, causing no results to be printed in the 'First N runs' section. Consider using max(1, runs / 100) or a different calculation.

Suggested change
let first_n_runs = runs / 100;
let first_n_runs = std::cmp::max(1, runs / 100);

Copilot uses AI. Check for mistakes.
Comment on lines +107 to +119
.or({
graph.verts.get(&meta.name).map(|vert_idx| {
unsafe {
LAGraph_RPQMatrix_label(
&mut mat as *mut grb::Matrix,
*vert_idx,
graph.verts.len(),
graph.verts.len(),
);
}
&mat
})
})
Copy link

Copilot AI Nov 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This closure references mat from the outer scope (line 103), but mat is shadowed on line 104. The reference &mat on line 117 would refer to the outer uninitialized matrix. This code would not compile as written, or has undefined behavior.

Copilot uses AI. Check for mistakes.
Comment on lines +136 to +137
LAGraph_RPQMatrix(&mut ans, plan as *mut RpqMatrixPlan, null_mut());
LAGraph_DestroyRpqMatrixPlan(plan);
Copy link

Copilot AI Nov 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The return value from LAGraph_RPQMatrix is ignored. Check the return code and propagate errors appropriately, similar to how LAGraph_MMRead is checked in graph.rs.

Suggested change
LAGraph_RPQMatrix(&mut ans, plan as *mut RpqMatrixPlan, null_mut());
LAGraph_DestroyRpqMatrixPlan(plan);
let status = LAGraph_RPQMatrix(&mut ans, plan as *mut RpqMatrixPlan, null_mut());
LAGraph_DestroyRpqMatrixPlan(plan);
if status != 0 {
return Err(format!("LAGraph_RPQMatrix failed with error code {}", status));
}

Copilot uses AI. Check for mistakes.
@georgiy-belyanin georgiy-belyanin merged commit 12f1f51 into main Nov 19, 2025
1 check passed
@georgiy-belyanin georgiy-belyanin deleted the basic-eval branch November 19, 2025 12:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants