Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
.git/
.worktrees/
target/
.tmp/
deps/LAGraph/build/
bench_criterion/
bench_results.json
bench_checkpoint.json
48 changes: 38 additions & 10 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ pathrex/
├── Cargo.toml # Crate manifest (edition 2024)
├── build.rs # Links LAGraph + LAGraphX; optionally regenerates FFI bindings
├── src/
│ ├── lib.rs # Modules: formats, graph, rpq, sparql, utils, lagraph_sys
│ ├── lib.rs # Modules: eval, formats, graph, rpq, sparql, utils, lagraph_sys
│ ├── main.rs # Binary entry point (placeholder)
│ ├── lagraph_sys.rs # FFI module — includes generated bindings
│ ├── lagraph_sys_generated.rs# Bindgen output (checked in, regenerated in CI)
Expand All @@ -24,8 +24,10 @@ pathrex/
│ │ ├── mod.rs # Core traits (GraphBuilder, GraphDecomposition, GraphSource,
│ │ │ # Backend, Graph<B>), error types, RAII wrappers, GrB init
│ │ └── inmemory.rs # InMemory marker, InMemoryBuilder, InMemoryGraph
│ ├── eval/
│ │ └── mod.rs # Evaluator, PreparedEvaluator, ResultCount traits
│ ├── rpq/
│ │ ├── mod.rs # RpqEvaluator (assoc. Result), RpqQuery, Endpoint, PathExpr, RpqError
│ │ ├── mod.rs # RPQ query types, RpqError, RPQ marker subtraits
│ │ ├── nfarpq.rs # NfaRpqEvaluator (LAGraph_RegularPathQuery)
│ │ └── rpqmatrix.rs # Matrix-plan RPQ evaluator
│ ├── sparql/
Expand Down Expand Up @@ -187,6 +189,18 @@ pub trait Backend {
- [`get_node_id(string_id)`](src/graph/mod.rs:200) / [`get_node_name(mapped_id)`](src/graph/mod.rs:203) — bidirectional string ↔ integer dictionary.
- [`num_nodes()`](src/graph/mod.rs:204) — total unique nodes.

### Generic evaluator abstraction (`src/eval/`)

[`src/eval/mod.rs`](src/eval/mod.rs) defines query-language-agnostic evaluator traits:

- [`Evaluator`](src/eval/mod.rs) uses associated types for `Query`, `Result`, `Error`, and
`Prepared`. The graph backend stays a method-level generic (`G: GraphDecomposition`) so one
evaluator type can run against any graph backend selected at the call site.
- [`PreparedEvaluator`](src/eval/mod.rs) represents prepared `(query, graph)` state that can be
executed repeatedly, which is used by benchmark timing loops.
- [`ResultCount`](src/eval/mod.rs) is separate from `Evaluator::Result`; only CLI runners that
need counts require this bound, leaving room for future evaluators with richer result types.

### InMemoryBuilder / InMemoryGraph

[`InMemoryBuilder`](src/graph/inmemory.rs:36) is the primary `GraphBuilder` implementation.
Expand Down Expand Up @@ -331,10 +345,11 @@ Key public items:
- [`RpqQuery`](src/rpq/mod.rs) — `{ subject, path, object }` using the types above;
[`strip_base(&mut self, base)`](src/rpq/mod.rs) removes a shared IRI prefix from
named endpoints and labels.
- [`RpqEvaluator`](src/rpq/mod.rs) — trait with associated type `Result` and
[`evaluate(query, graph)`](src/rpq/mod.rs) taking `&RpqQuery` and
[`GraphDecomposition`], returning `Result<Self::Result, RpqError>`.
Each concrete evaluator exposes its own output type (see below).
- [`RpqEvaluator`](src/rpq/mod.rs) — marker subtrait over
[`Evaluator<Query = RpqQuery, Error = RpqError>`](src/eval/mod.rs), preserving the RPQ-facing
trait name while the generic evaluator hierarchy lives in `src/eval/`.
- [`PreparedRpq`](src/rpq/mod.rs) — marker subtrait over
[`PreparedEvaluator<Error = RpqError>`](src/eval/mod.rs).
- [`RpqError`](src/rpq/mod.rs) — unified error type for RPQ parsing and evaluation:
`Parse` (SPARQL syntax), `Extract` (query extraction), `UnsupportedPath`,
`VertexNotFound`, and `Graph` (wraps [`GraphError`](src/graph/mod.rs) for
Expand Down Expand Up @@ -378,10 +393,11 @@ Key public items:
- [`RpqQuery`](src/rpq/mod.rs) — `{ subject, path, object }` using the types above;
[`strip_base(&mut self, base)`](src/rpq/mod.rs) removes a shared IRI prefix from
named endpoints and labels.
- [`RpqEvaluator`](src/rpq/mod.rs) — trait with associated type `Result` and
[`evaluate(query, graph)`](src/rpq/mod.rs) taking `&RpqQuery` and
[`GraphDecomposition`], returning `Result<Self::Result, RpqError>`.
Each concrete evaluator exposes its own output type (see below).
- [`RpqEvaluator`](src/rpq/mod.rs) — marker subtrait over
[`Evaluator<Query = RpqQuery, Error = RpqError>`](src/eval/mod.rs), preserving the RPQ-facing
trait name while the generic evaluator hierarchy lives in `src/eval/`.
- [`PreparedRpq`](src/rpq/mod.rs) — marker subtrait over
[`PreparedEvaluator<Error = RpqError>`](src/eval/mod.rs).
- [`RpqError`](src/rpq/mod.rs) — unified error type for RPQ parsing and evaluation:
`Parse` (SPARQL syntax), `Extract` (query extraction), `UnsupportedPath`,
`VertexNotFound`, and `Graph` (wraps [`GraphError`](src/graph/mod.rs) for
Expand Down Expand Up @@ -423,6 +439,18 @@ over label adjacency matrices and runs [`LAGraph_RPQMatrix`]. It returns
Subject/object do not filter the matrix; a named subject is only validated to exist.
Bound objects are not supported yet ([`RpqError::UnsupportedPath`]).

### CLI dispatch (`src/cli/dispatch.rs`)

With the `bench` feature enabled, [`src/cli/dispatch.rs`](src/cli/dispatch.rs) is the single
mapping from [`Algo`](src/cli/args.rs) variants to concrete evaluator types. `dispatch_query`
and `dispatch_bench` each perform one exhaustive `match` per requested algorithm, then call
generic runners (`run_query_for_evaluator<E>` and `run_bench_for_evaluator<E>`) that are
monomorphized for the selected evaluator.

Adding a new algorithm requires a new `Algo` variant, its `Display` arm, one `dispatch_query`
arm, one `dispatch_bench` arm, an `impl Evaluator` for the evaluator type, and an
`impl ResultCount` for any result type used by CLI count reporting.

### FFI layer

[`lagraph_sys`](src/lagraph_sys.rs) exposes raw C bindings for GraphBLAS and
Expand Down
13 changes: 13 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,24 @@ rustfst = "1.2"
spargebra = "0.4.6"
thiserror = "1.0"

clap = { version = "4", features = ["derive"], optional = true }
serde = { version = "1", features = ["derive"], optional = true }
serde_json = { version = "1", optional = true }
chrono = { version = "0.4", features = ["serde"], optional = true }
criterion = { version = "0.5", optional = true }
tempfile = { version = "3", optional = true }

[features]
regenerate-bindings = ["bindgen"]
bench = ["clap", "serde", "serde_json", "chrono", "criterion", "tempfile"]

[dev-dependencies]
tempfile = "3"

[build-dependencies]
bindgen = { version = "0.71", optional = true }

[[bin]]
name = "pathrex"
path = "src/bin/pathrex.rs"
required-features = ["bench"]
49 changes: 49 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
FROM rust:1-bookworm AS builder

RUN apt-get update \
&& apt-get install -y --no-install-recommends \
build-essential \
ca-certificates \
clang \
cmake \
git \
libclang-dev \
make \
pkg-config \
&& rm -rf /var/lib/apt/lists/*

WORKDIR /src

COPY . .

RUN git clone --depth 1 https://github.com/DrTimothyAldenDavis/GraphBLAS.git /tmp/GraphBLAS \
&& make -C /tmp/GraphBLAS compact \
&& make -C /tmp/GraphBLAS install \
&& mkdir -p deps/LAGraph/build \
&& make -C deps/LAGraph \
&& cargo build --release --bin pathrex --features "bench,regenerate-bindings"

FROM debian:bookworm-slim AS runtime

RUN apt-get update \
&& apt-get install -y --no-install-recommends \
ca-certificates \
gcc \
libc6-dev \
libgomp1 \
&& rm -rf /var/lib/apt/lists/*

WORKDIR /work

COPY --from=builder /src/target/release/pathrex /usr/local/bin/pathrex
COPY --from=builder /usr/local/lib/libgraphblas.so* /usr/local/lib/
COPY --from=builder /src/deps/LAGraph/build/src/liblagraph.so* /usr/local/lib/
COPY --from=builder /src/deps/LAGraph/build/experimental/liblagraphx.so* /usr/local/lib/
COPY --from=builder /src/docker/docker-entrypoint.sh /usr/local/bin/docker-entrypoint.sh

RUN chmod +x /usr/local/bin/docker-entrypoint.sh

ENV LD_LIBRARY_PATH=/usr/local/lib

ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"]
CMD ["--help"]
2 changes: 2 additions & 0 deletions build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ fn regenerate_bindings() {
.allowlist_item("GrB_Info")
.allowlist_function("GrB_Matrix_new")
.allowlist_function("GrB_Matrix_nvals")
.allowlist_function("GrB_Matrix_dup")
.allowlist_function("GrB_Matrix_free")
.allowlist_function("GrB_Matrix_extractElement_BOOL")
.allowlist_function("GrB_Matrix_build_BOOL")
Expand All @@ -89,6 +90,7 @@ fn regenerate_bindings() {
.allowlist_function("LAGraph_Cached_AT")
.allowlist_function("LAGraph_MMRead")
.allowlist_function("LAGraph_RPQMatrix")
.allowlist_function("LAGraph_RPQMatrix_reduce")
.allowlist_function("LAGraph_DestroyRpqMatrixPlan")
.allowlist_function("LAGraph_RPQMatrix_label")
.allowlist_function("LAGraph_RPQMatrix_Free")
Expand Down
42 changes: 42 additions & 0 deletions docker/docker-entrypoint.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
#!/usr/bin/env bash
set -euo pipefail

pathrex_bin="${PATHREX_BIN:-/usr/local/bin/pathrex}"

if [ "${1-}" = "bench" ]; then
shift
has_output=false
has_checkpoint=false
has_criterion_dir=false
args=("$@")

for arg in "${args[@]}"; do
case "$arg" in
-o|-o*|--output|--output=*)
has_output=true
;;
-c|-c*|--checkpoint|--checkpoint=*)
has_checkpoint=true
;;
--criterion-dir|--criterion-dir=*)
has_criterion_dir=true
;;
esac
done

if [ "$has_output" = false ]; then
args+=(--output /results/bench_results.json)
fi

if [ "$has_checkpoint" = false ]; then
args+=(--checkpoint /results/bench_checkpoint.json)
fi

if [ "$has_criterion_dir" = false ]; then
args+=(--criterion-dir /results/criterion)
fi

exec "$pathrex_bin" bench "${args[@]}"
fi

exec "$pathrex_bin" "$@"
Loading
Loading